760 lines
34 KiB
C#
760 lines
34 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Barotrauma.Extensions;
|
|
using Microsoft.Xna.Framework.Input;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
public class GUIMessageBox : GUIFrame
|
|
{
|
|
public readonly static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
|
|
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,
|
|
Vote,
|
|
Hint,
|
|
Tutorial,
|
|
Warning // Keep this last so that it's always drawn in front
|
|
}
|
|
|
|
private bool IsAnimated => type == Type.InGame || type == Type.Hint || type == Type.Tutorial;
|
|
|
|
public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
|
|
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 Identifier Tag { get; private set; }
|
|
public bool Closed { get; private set; }
|
|
|
|
public bool DisplayInLoadingScreens;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
public bool Draggable { get; set; }
|
|
public Vector2 DraggingPosition = Vector2.Zero;
|
|
|
|
public GUIImage BackgroundIcon { get; private set; }
|
|
private GUIImage newBackgroundIcon;
|
|
|
|
/// <summary>
|
|
/// Close the message box automatically after enough time has passed (<see cref="inGameCloseTime"/>)
|
|
/// </summary>
|
|
public bool AutoClose;
|
|
|
|
private float openState;
|
|
private float iconState;
|
|
private bool iconSwitching;
|
|
private bool closing;
|
|
|
|
private readonly Type type;
|
|
|
|
/// <summary>
|
|
/// Close the message box automatically if the condition is met
|
|
/// </summary>
|
|
private readonly Func<bool> autoCloseCondition;
|
|
|
|
public bool FlashOnAutoCloseCondition { get; set; }
|
|
|
|
public Action OnEnterPressed { get; set; }
|
|
|
|
public Type MessageBoxType => type;
|
|
|
|
/// <summary>
|
|
/// If enabled, the box is always drawn in front of all other elements.
|
|
/// </summary>
|
|
public bool DrawOnTop;
|
|
|
|
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
|
|
|
|
public GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize = null, Point? minSize = null, Type type = Type.Default)
|
|
: this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize, type: type)
|
|
{
|
|
this.Buttons[0].OnClicked = Close;
|
|
OnEnterPressed = () =>
|
|
{
|
|
Buttons[0].OnClicked(Buttons[0], Buttons[0].UserData);
|
|
};
|
|
}
|
|
|
|
public GUIMessageBox(RichString headerText, RichString text, LocalizedString[] buttons,
|
|
Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "",
|
|
Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, Func<bool> autoCloseCondition = null, bool hideCloseButton = false)
|
|
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUIStyle.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
|
|
{
|
|
int width = (int)(DefaultWidth * type switch
|
|
{
|
|
Type.Default => 1.0f,
|
|
Type.Hint => 1.25f,
|
|
_ => 1.5f
|
|
});
|
|
int 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);
|
|
}
|
|
}
|
|
|
|
if (backgroundIcon != null)
|
|
{
|
|
BackgroundIcon = new GUIImage(new RectTransform(backgroundIcon.size.ToPoint(), RectTransform), backgroundIcon)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
Color = Color.Transparent
|
|
};
|
|
}
|
|
|
|
Anchor anchor = type switch
|
|
{
|
|
Type.InGame => Anchor.TopCenter,
|
|
Type.Tutorial => Anchor.TopCenter,
|
|
Type.Hint => Anchor.TopRight,
|
|
Type.Vote => Anchor.TopRight,
|
|
_ => Anchor.Center
|
|
};
|
|
|
|
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, anchor) { IsFixedSize = false }, style: null);
|
|
if (type == Type.Vote)
|
|
{
|
|
int offset = GUI.IntScale(64);
|
|
InnerFrame.RectTransform.ScreenSpaceOffset = new Point(-offset, offset);
|
|
CanBeFocused = false;
|
|
}
|
|
GUIStyle.Apply(InnerFrame, "", this);
|
|
this.type = type;
|
|
Tag = tag.ToIdentifier();
|
|
|
|
#warning TODO: These should be broken into separate methods at least
|
|
if (type == Type.Default || type == Type.Vote || type == Type.Warning)
|
|
{
|
|
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: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
|
GUIStyle.Apply(Header, "", this);
|
|
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
|
|
|
if (!text.IsNullOrWhiteSpace())
|
|
{
|
|
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
|
GUIStyle.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 = GUIStyle.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<GUIButton>(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);
|
|
}
|
|
GUITextBlock.AutoScaleAndNormalize(Buttons.Select(btn => btn.TextBlock));
|
|
}
|
|
else if (type == Type.InGame || type == Type.Tutorial)
|
|
{
|
|
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
|
|
CanBeFocused = false;
|
|
AutoClose = type == Type.InGame;
|
|
GUIStyle.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);
|
|
}
|
|
else if (iconStyle != string.Empty)
|
|
{
|
|
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), iconStyle, scaleToFit: true);
|
|
}
|
|
|
|
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
|
|
|
|
if (!hideCloseButton)
|
|
{
|
|
var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null);
|
|
Buttons = new List<GUIButton>(1)
|
|
{
|
|
new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
|
|
style: "UIToggleButton")
|
|
{
|
|
OnClicked = Close,
|
|
UserData = UIHighlightAction.ElementId.MessageBoxCloseButton
|
|
}
|
|
};
|
|
InputType? closeInput = null;
|
|
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
|
|
{
|
|
closeInput = InputType.Use;
|
|
}
|
|
else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
|
|
{
|
|
closeInput = InputType.Select;
|
|
}
|
|
if (closeInput.HasValue)
|
|
{
|
|
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
|
|
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
|
|
{
|
|
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
|
|
{
|
|
GUIButton btn = component as GUIButton;
|
|
btn?.OnClicked(btn, btn.UserData);
|
|
btn?.Flash(GUIStyle.Green);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Buttons.Clear();
|
|
}
|
|
|
|
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, textColor: GUIStyle.TextColorBright);
|
|
GUIStyle.Apply(Header, "", this);
|
|
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
|
|
|
if (!text.IsNullOrWhiteSpace())
|
|
{
|
|
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
|
GUIStyle.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 (headerText.IsNullOrWhiteSpace())
|
|
{
|
|
Content.ChildAnchor = Anchor.Center;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (!hideCloseButton)
|
|
{
|
|
Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
|
|
}
|
|
}
|
|
else if (type == Type.Hint)
|
|
{
|
|
CanBeFocused = false;
|
|
GUIStyle.Apply(InnerFrame, "", this);
|
|
|
|
Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
|
|
var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
|
{
|
|
AbsoluteSpacing = absoluteSpacing.Y,
|
|
Stretch = true
|
|
};
|
|
|
|
var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform),
|
|
isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
|
{
|
|
Stretch = true,
|
|
RelativeSpacing = 0.02f
|
|
};
|
|
|
|
int iconMaxHeight = 0;
|
|
if (icon != null)
|
|
{
|
|
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
|
|
iconMaxHeight = (int)Icon.Sprite.size.Y;
|
|
}
|
|
else
|
|
{
|
|
bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle);
|
|
Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform),
|
|
iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true);
|
|
if (!iconStyleDefined)
|
|
{
|
|
Icon.Color = Color.Orange;
|
|
}
|
|
iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40);
|
|
}
|
|
|
|
iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
|
|
int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
|
|
Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight);
|
|
Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight);
|
|
|
|
Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform))
|
|
{
|
|
AbsoluteSpacing = absoluteSpacing.Y,
|
|
};
|
|
|
|
var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null)
|
|
{
|
|
CanBeFocused = true
|
|
};
|
|
|
|
var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft))
|
|
{
|
|
CanBeFocused = true,
|
|
Stretch = true
|
|
};
|
|
Vector2 tickBoxRelativeSize = new Vector2(1.0f, 0.5f);
|
|
var dontShowAgainTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
|
|
TextManager.Get("hintmessagebox.dontshowagain"))
|
|
{
|
|
ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
|
|
UserData = "dontshowagain"
|
|
};
|
|
var disableHintsTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
|
|
TextManager.Get("hintmessagebox.disablehints"))
|
|
{
|
|
ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
|
|
UserData = "disablehints"
|
|
};
|
|
|
|
Buttons = new List<GUIButton>(1)
|
|
{
|
|
new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight),
|
|
text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall")
|
|
{
|
|
OnClicked = Close
|
|
}
|
|
};
|
|
|
|
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
|
|
GUIStyle.Apply(Header, "", this);
|
|
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
|
|
|
if (!text.IsNullOrWhiteSpace())
|
|
{
|
|
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
|
GUIStyle.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 (headerText.IsNullOrWhiteSpace())
|
|
{
|
|
Header.RectTransform.Parent = null;
|
|
Content.ChildAnchor = Anchor.Center;
|
|
}
|
|
}
|
|
|
|
if (height == 0)
|
|
{
|
|
height = absoluteSpacing.Y;
|
|
int upperContainerHeight = absoluteSpacing.Y;
|
|
if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; }
|
|
if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; }
|
|
upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
|
|
height += upperContainerHeight;
|
|
height += absoluteSpacing.Y;
|
|
int bottomContainerHeight = dontShowAgainTickBox.Rect.Height + disableHintsTickBox.Rect.Height;
|
|
height += bottomContainerHeight;
|
|
height += absoluteSpacing.Y;
|
|
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
|
|
|
|
InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
|
|
verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
|
|
float upperContainerRelativeHeight = (float)upperContainerHeight / (upperContainerHeight + bottomContainerHeight);
|
|
topHorizontalLayoutGroup.RectTransform.RelativeSize = new Vector2(topHorizontalLayoutGroup.RectTransform.RelativeSize.X, upperContainerRelativeHeight);
|
|
bottomContainer.RectTransform.RelativeSize = new Vector2(bottomContainer.RectTransform.RelativeSize.X, 1.0f - upperContainerRelativeHeight);
|
|
verticalLayoutGroup.Recalculate();
|
|
topHorizontalLayoutGroup.Recalculate();
|
|
Content.Recalculate();
|
|
tickBoxLayoutGroup.Recalculate();
|
|
}
|
|
|
|
InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height);
|
|
|
|
Point GetVerticalLayoutGroupSize()
|
|
{
|
|
return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2);
|
|
}
|
|
}
|
|
|
|
this.autoCloseCondition = autoCloseCondition;
|
|
|
|
MessageBoxes.Add(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to create a message box of Hint type
|
|
/// </summary>
|
|
public GUIMessageBox(Identifier hintIdentifier, LocalizedString text, Sprite icon) : this("", text, Array.Empty<LocalizedString>(), textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
|
|
{
|
|
if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
|
|
{
|
|
dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain;
|
|
dontShowAgainTickBox.UserData = hintIdentifier;
|
|
}
|
|
if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox)
|
|
{
|
|
disableHintsTickBox.OnSelected = HintManager.OnDisableHints;
|
|
disableHintsTickBox.UserData = hintIdentifier;
|
|
}
|
|
}
|
|
|
|
private static Type[] messageBoxTypes;
|
|
|
|
public static void AddActiveToGUIUpdateList()
|
|
{
|
|
messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type));
|
|
|
|
foreach (var type in messageBoxTypes)
|
|
{
|
|
// Don't display hints when HUD is disabled
|
|
if (type == Type.Hint && GUI.DisableHUD) { continue; }
|
|
|
|
for (int i = 0; i < MessageBoxes.Count; i++)
|
|
{
|
|
if (MessageBoxes[i] == null) { continue; }
|
|
if (MessageBoxes[i] is not GUIMessageBox messageBox)
|
|
{
|
|
if (type == Type.Default)
|
|
{
|
|
// Message box not of type GUIMessageBox is likely the round summary
|
|
MessageBoxes[i].AddToGUIUpdateList();
|
|
if (MessageBoxes[i].UserData is not RoundSummary) { break; }
|
|
}
|
|
continue;
|
|
}
|
|
if (messageBox.type != type) { continue; }
|
|
if (!messageBox.DisplayInLoadingScreens && GameMain.Instance.LoadingScreenOpen)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// These are handled separately in GUI.HandlePersistingElements()
|
|
if (messageBox.DrawOnTop) { continue; }
|
|
|
|
messageBox.AddToGUIUpdateList();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetBackgroundIcon(Sprite icon)
|
|
{
|
|
if (icon == null) { return; }
|
|
if (icon == BackgroundIcon?.Sprite) { return; }
|
|
GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
|
|
{
|
|
IgnoreLayoutGroups = true,
|
|
Color = Color.Transparent
|
|
};
|
|
|
|
if (newBackgroundIcon != null)
|
|
{
|
|
RemoveChild(newBackgroundIcon);
|
|
newBackgroundIcon = null;
|
|
}
|
|
newBackgroundIcon = newIcon;
|
|
}
|
|
|
|
protected override void Update(float deltaTime)
|
|
{
|
|
if (PlayerInput.KeyHit(Keys.Enter))
|
|
{
|
|
OnEnterPressed?.Invoke();
|
|
}
|
|
|
|
if (Draggable)
|
|
{
|
|
GUIComponent parent = GUI.MouseOn?.Parent?.Parent;
|
|
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton || GUI.MouseOn is GUIColorPicker || GUI.MouseOn is GUITextBox || parent is GUITextBox))
|
|
{
|
|
GUI.MouseCursor = CursorState.Move;
|
|
if (PlayerInput.PrimaryMouseButtonDown())
|
|
{
|
|
DraggingPosition = RectTransform.ScreenSpaceOffset.ToVector2() - PlayerInput.MousePosition;
|
|
}
|
|
}
|
|
|
|
if (PlayerInput.PrimaryMouseButtonHeld() && DraggingPosition != Vector2.Zero)
|
|
{
|
|
GUI.MouseCursor = CursorState.Dragging;
|
|
RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition + DraggingPosition).ToPoint();
|
|
}
|
|
else
|
|
{
|
|
DraggingPosition = Vector2.Zero;
|
|
}
|
|
}
|
|
|
|
if (IsAnimated)
|
|
{
|
|
Vector2 initialPos, defaultPos, endPos;
|
|
if (type == Type.InGame || type == Type.Tutorial)
|
|
{
|
|
initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
|
|
defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
|
|
endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
|
|
}
|
|
else
|
|
{
|
|
initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height);
|
|
defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.ButtonAreaTop.Height + GUI.IntScale(64));
|
|
endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y);
|
|
}
|
|
|
|
if (!closing)
|
|
{
|
|
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
|
|
InnerFrame.RectTransform.AbsoluteOffset = step;
|
|
if (BackgroundIcon != null)
|
|
{
|
|
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
|
|
if (!MathUtils.NearlyEqual(openState, 1.0f))
|
|
{
|
|
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
|
|
}
|
|
}
|
|
if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
|
|
{
|
|
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
|
|
}
|
|
|
|
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
|
|
{
|
|
inGameCloseTimer += deltaTime;
|
|
}
|
|
|
|
if (inGameCloseTimer >= inGameCloseTime)
|
|
{
|
|
Close();
|
|
}
|
|
else if (autoCloseCondition != null && autoCloseCondition())
|
|
{
|
|
Close();
|
|
if (FlashOnAutoCloseCondition)
|
|
{
|
|
InnerFrame.Flash(GUIStyle.Green);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
openState += deltaTime * 2.0f;
|
|
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
|
|
InnerFrame.RectTransform.AbsoluteOffset = step;
|
|
if (BackgroundIcon != null)
|
|
{
|
|
BackgroundIcon.Color *= 0.9f;
|
|
}
|
|
if (openState >= 2.0f)
|
|
{
|
|
Parent?.RemoveChild(this);
|
|
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
|
|
}
|
|
}
|
|
|
|
if (newBackgroundIcon != null)
|
|
{
|
|
if (!iconSwitching)
|
|
{
|
|
if (BackgroundIcon != null)
|
|
{
|
|
BackgroundIcon.Color *= 0.9f;
|
|
if (BackgroundIcon.Color.A == 0)
|
|
{
|
|
BackgroundIcon = null;
|
|
iconSwitching = true;
|
|
RemoveChild(BackgroundIcon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iconSwitching = true;
|
|
}
|
|
iconState = 0;
|
|
}
|
|
else
|
|
{
|
|
newBackgroundIcon.SetAsFirstChild();
|
|
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
|
|
newBackgroundIcon.Color = Color.Lerp(Color.Transparent, Color.White, iconState);
|
|
if (newBackgroundIcon.Color.A == 255)
|
|
{
|
|
BackgroundIcon = newBackgroundIcon;
|
|
BackgroundIcon.SetAsFirstChild();
|
|
newBackgroundIcon = null;
|
|
iconSwitching = false;
|
|
}
|
|
|
|
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
if (IsAnimated)
|
|
{
|
|
closing = true;
|
|
}
|
|
else
|
|
{
|
|
Parent?.RemoveChild(this);
|
|
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
|
|
}
|
|
|
|
Closed = true;
|
|
}
|
|
|
|
public bool Close(GUIButton button, object obj)
|
|
{
|
|
RectTransform.Parent = null;
|
|
Close();
|
|
return true;
|
|
}
|
|
|
|
public static void CloseAll()
|
|
{
|
|
MessageBoxes.Clear();
|
|
}
|
|
|
|
public static void Close(Identifier tag)
|
|
{
|
|
foreach (var messageBox in MessageBoxes)
|
|
{
|
|
if (messageBox is GUIMessageBox mb && mb.Tag == tag)
|
|
{
|
|
mb.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void Close(string tag) => Close(tag.ToIdentifier());
|
|
|
|
/// <summary>
|
|
/// Parent does not matter. It's overridden.
|
|
/// </summary>
|
|
public void AddButton(RectTransform rectT, string text, GUIButton.OnClickedHandler onClick)
|
|
{
|
|
rectT.Parent = RectTransform;
|
|
Buttons.Add(new GUIButton(rectT, text) { OnClicked = onClick });
|
|
}
|
|
|
|
public static GUIMessageBox CreateLoadingBox(LocalizedString text, (LocalizedString Label, Action<GUIMessageBox> Action)[] buttons = null, Vector2? relativeSize = null)
|
|
{
|
|
buttons ??= Array.Empty<(LocalizedString Label, Action<GUIMessageBox> Action)>();
|
|
var relativeSizeFallback = relativeSize ?? (0.7f, 0.5f);
|
|
var newMessageBox = new GUIMessageBox(
|
|
headerText: "",
|
|
text: "",
|
|
relativeSize: relativeSizeFallback,
|
|
buttons: buttons.Select(b => b.Label).ToArray());
|
|
newMessageBox.InnerFrame.RectTransform.ScaleBasis = ScaleBasis.BothHeight;
|
|
|
|
for (int i = 0; i < buttons.Length; i++)
|
|
{
|
|
var capturedIndex = i;
|
|
newMessageBox.Buttons[i].OnClicked = (_, _) =>
|
|
{
|
|
buttons[capturedIndex].Action(newMessageBox);
|
|
return false;
|
|
};
|
|
}
|
|
|
|
const float throbberSize = 0.25f;
|
|
|
|
new GUITextBlock(
|
|
new RectTransform((0.9f, 0f), newMessageBox.InnerFrame.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = (0f, -throbberSize * 0.5f) },
|
|
text: text, textAlignment: Alignment.Center, wrap: true);
|
|
|
|
// Throbber
|
|
new GUICustomComponent(
|
|
new RectTransform(Vector2.One * throbberSize, newMessageBox.InnerFrame.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.BothHeight),
|
|
onDraw: static (sb, component) =>
|
|
{
|
|
GUIStyle.GenericThrobber.Draw(
|
|
sb,
|
|
spriteIndex: (int)(Timing.TotalTime * 20f) % GUIStyle.GenericThrobber.FrameCount,
|
|
pos: component.Rect.Center.ToVector2(),
|
|
color: Color.White,
|
|
origin: GUIStyle.GenericThrobber.FrameSize.ToVector2() * 0.5f,
|
|
rotate: 0f,
|
|
scale: component.Rect.Size.ToVector2() / GUIStyle.GenericThrobber.FrameSize.ToVector2());
|
|
});
|
|
|
|
MessageBoxes.Remove(newMessageBox);
|
|
MessageBoxes.Insert(0, newMessageBox);
|
|
return newMessageBox;
|
|
}
|
|
}
|
|
}
|