Client LuaCsForBarotrauma
GUIMessageBox.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
6 using Microsoft.Xna.Framework.Input;
7 
8 namespace Barotrauma
9 {
10  public class GUIMessageBox : GUIFrame
11  {
12  public readonly static List<GUIComponent> MessageBoxes = new List<GUIComponent>();
13  private static int DefaultWidth
14  {
15  get { return Math.Max(400, (int)(400 * (GameMain.GraphicsWidth / GUI.ReferenceResolution.X))); }
16  }
17 
18  private float inGameCloseTimer = 0.0f;
19  private const float inGameCloseTime = 15f;
20 
21  public enum Type
22  {
23  Default,
24  InGame,
25  Vote,
26  Hint,
27  Tutorial,
28  Warning // Keep this last so that it's always drawn in front
29  }
30 
31  private bool IsAnimated => type == Type.InGame || type == Type.Hint || type == Type.Tutorial;
32 
33  public List<GUIButton> Buttons { get; private set; } = new List<GUIButton>();
34  public GUILayoutGroup Content { get; private set; }
35  public GUIFrame InnerFrame { get; private set; }
36  public GUITextBlock Header { get; private set; }
37  public GUITextBlock Text { get; private set; }
38  public Identifier Tag { get; private set; }
39  public bool Closed { get; private set; }
40 
42 
43  public GUIImage Icon
44  {
45  get;
46  private set;
47  }
48 
49  public Color IconColor
50  {
51  get { return Icon == null ? Color.White : Icon.Color; }
52  set
53  {
54  if (Icon == null) { return; }
55  Icon.Color = value;
56  }
57  }
58 
59  public bool Draggable { get; set; }
60  public Vector2 DraggingPosition = Vector2.Zero;
61 
62  public GUIImage BackgroundIcon { get; private set; }
63  private GUIImage newBackgroundIcon;
64 
68  public bool AutoClose;
69 
70  private float openState;
71  private float iconState;
72  private bool iconSwitching;
73  private bool closing;
74 
75  private readonly Type type;
76 
80  private readonly Func<bool> autoCloseCondition;
81 
82  public bool FlashOnAutoCloseCondition { get; set; }
83 
84  public Action OnEnterPressed { get; set; }
85 
86  public Type MessageBoxType => type;
87 
91  public bool DrawOnTop;
92 
93  public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
94 
95  public GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize = null, Point? minSize = null, Type type = Type.Default)
96  : this(headerText, text, new LocalizedString[] { "OK" }, relativeSize, minSize, type: type)
97  {
98  this.Buttons[0].OnClicked = Close;
99  OnEnterPressed = () =>
100  {
101  Buttons[0].OnClicked(Buttons[0], Buttons[0].UserData);
102  };
103  }
104 
105  public GUIMessageBox(RichString headerText, RichString text, LocalizedString[] buttons,
106  Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "",
107  Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null, Func<bool> autoCloseCondition = null, bool hideCloseButton = false)
108  : base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUIStyle.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
109  {
110  int width = (int)(DefaultWidth * type switch
111  {
112  Type.Default => 1.0f,
113  Type.Hint => 1.25f,
114  _ => 1.5f
115  });
116  int height = 0;
117 
118  if (relativeSize.HasValue)
119  {
120  width = (int)(GameMain.GraphicsWidth * relativeSize.Value.X);
121  height = (int)(GameMain.GraphicsHeight * relativeSize.Value.Y);
122  }
123  if (minSize.HasValue)
124  {
125  width = Math.Max(width, minSize.Value.X);
126  if (height > 0)
127  {
128  height = Math.Max(height, minSize.Value.Y);
129  }
130  }
131 
132  if (backgroundIcon != null)
133  {
134  BackgroundIcon = new GUIImage(new RectTransform(backgroundIcon.size.ToPoint(), RectTransform), backgroundIcon)
135  {
136  IgnoreLayoutGroups = true,
137  Color = Color.Transparent
138  };
139  }
140 
141  Anchor anchor = type switch
142  {
143  Type.InGame => Anchor.TopCenter,
144  Type.Tutorial => Anchor.TopCenter,
145  Type.Hint => Anchor.TopRight,
146  Type.Vote => Anchor.TopRight,
147  _ => Anchor.Center
148  };
149 
150  InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, anchor) { IsFixedSize = false }, style: null);
151  if (type == Type.Vote)
152  {
153  int offset = GUI.IntScale(64);
154  InnerFrame.RectTransform.ScreenSpaceOffset = new Point(-offset, offset);
155  CanBeFocused = false;
156  }
157  GUIStyle.Apply(InnerFrame, "", this);
158  this.type = type;
159  Tag = tag.ToIdentifier();
160 
161  #warning TODO: These should be broken into separate methods at least
162  if (type == Type.Default || type == Type.Vote || type == Type.Warning)
163  {
164  Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
165 
166  Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
167  headerText, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
168  GUIStyle.Apply(Header, "", this);
169  Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
170 
171  if (!text.IsNullOrWhiteSpace())
172  {
173  Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
174  GUIStyle.Apply(Text, "", this);
176  new Point(Text.Rect.Width, Text.Rect.Height);
178  }
179 
180  var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), Content.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter)
181  {
182  AbsoluteSpacing = 5,
183  IgnoreLayoutGroups = true
184  };
185 
186  int buttonSize = 35;
187  var buttonStyle = GUIStyle.GetComponentStyle("GUIButton");
188  if (buttonStyle != null && buttonStyle.Height.HasValue)
189  {
190  buttonSize = buttonStyle.Height.Value;
191  }
192 
193  buttonContainer.RectTransform.NonScaledSize = buttonContainer.RectTransform.MinSize = buttonContainer.RectTransform.MaxSize =
194  new Point(buttonContainer.Rect.Width, (int)((buttonSize + 5) * buttons.Length));
195  buttonContainer.RectTransform.IsFixedSize = true;
196 
197  if (height == 0)
198  {
199  height += Header.Rect.Height + Content.AbsoluteSpacing;
200  height += (Text == null ? 0 : Text.Rect.Height) + Content.AbsoluteSpacing;
201  height += buttonContainer.Rect.Height + 20;
202  if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
203 
205  new Point(InnerFrame.Rect.Width, (int)Math.Max(height / Content.RectTransform.RelativeSize.Y, height + (int)(50 * GUI.yScale)));
207  new Point(Content.Rect.Width, height);
208  }
209 
210  Buttons = new List<GUIButton>(buttons.Length);
211  for (int i = 0; i < buttons.Length; i++)
212  {
213  var button = new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f / buttons.Length), buttonContainer.RectTransform), buttons[i]);
214  Buttons.Add(button);
215  }
216  GUITextBlock.AutoScaleAndNormalize(Buttons.Select(btn => btn.TextBlock));
217  }
218  else if (type == Type.InGame || type == Type.Tutorial)
219  {
221  CanBeFocused = false;
222  AutoClose = type == Type.InGame;
223  GUIStyle.Apply(InnerFrame, "", this);
224 
225  var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
226  isHorizontal: true, childAnchor: Anchor.CenterLeft)
227  {
228  Stretch = true,
229  RelativeSpacing = 0.02f
230  };
231  if (icon != null)
232  {
233  Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
234  }
235  else if (iconStyle != string.Empty)
236  {
237  Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), iconStyle, scaleToFit: true);
238  }
239 
240  Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
241 
242  if (!hideCloseButton)
243  {
244  var buttonContainer = new GUIFrame(new RectTransform(new Vector2(0.15f, 1.0f), horizontalLayoutGroup.RectTransform), style: null);
245  Buttons = new List<GUIButton>(1)
246  {
247  new GUIButton(new RectTransform(new Vector2(0.3f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
248  style: "UIToggleButton")
249  {
250  OnClicked = Close,
251  UserData = UIHighlightAction.ElementId.MessageBoxCloseButton
252  }
253  };
254  InputType? closeInput = null;
255  if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Use].MouseButton == MouseButton.None)
256  {
257  closeInput = InputType.Use;
258  }
259  else if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.Select].MouseButton == MouseButton.None)
260  {
261  closeInput = InputType.Select;
262  }
263  if (closeInput.HasValue)
264  {
265  Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
266  Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
267  {
268  if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
269  {
270  GUIButton btn = component as GUIButton;
271  btn?.OnClicked(btn, btn.UserData);
272  btn?.Flash(GUIStyle.Green);
273  }
274  };
275  }
276  }
277  else
278  {
279  Buttons.Clear();
280  }
281 
282  Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true, textColor: GUIStyle.TextColorBright);
283  GUIStyle.Apply(Header, "", this);
284  Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
285 
286  if (!text.IsNullOrWhiteSpace())
287  {
288  Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
289  GUIStyle.Apply(Text, "", this);
292  new Point(Text.Rect.Width, Text.Rect.Height);
294  if (headerText.IsNullOrWhiteSpace())
295  {
296  Content.ChildAnchor = Anchor.Center;
297  }
298  }
299 
300  if (height == 0)
301  {
302  height += Header.Rect.Height + Content.AbsoluteSpacing;
303  height += (Text == null ? 0 : Text.Rect.Height) + Content.AbsoluteSpacing;
304  if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
305 
307  new Point(InnerFrame.Rect.Width, (int)Math.Max(height / Content.RectTransform.RelativeSize.Y, height + (int)(50 * GUI.yScale)));
309  new Point(Content.Rect.Width, height);
310  }
311  if (!hideCloseButton)
312  {
313  Buttons[0].RectTransform.MaxSize = new Point((int)(0.4f * Buttons[0].Rect.Y), Buttons[0].Rect.Y);
314  }
315  }
316  else if (type == Type.Hint)
317  {
318  CanBeFocused = false;
319  GUIStyle.Apply(InnerFrame, "", this);
320 
321  Point absoluteSpacing = GUIStyle.ItemFrameMargin.Multiply(1.0f / 5.0f);
322  var verticalLayoutGroup = new GUILayoutGroup(new RectTransform(GetVerticalLayoutGroupSize(), parent: InnerFrame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
323  {
324  AbsoluteSpacing = absoluteSpacing.Y,
325  Stretch = true
326  };
327 
328  var topHorizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.7f), verticalLayoutGroup.RectTransform),
329  isHorizontal: true, childAnchor: Anchor.CenterLeft)
330  {
331  Stretch = true,
332  RelativeSpacing = 0.02f
333  };
334 
335  int iconMaxHeight = 0;
336  if (icon != null)
337  {
338  Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
339  iconMaxHeight = (int)Icon.Sprite.size.Y;
340  }
341  else
342  {
343  bool iconStyleDefined = !string.IsNullOrEmpty(iconStyle);
344  Icon = new GUIImage(new RectTransform(new Vector2(0.15f, 0.95f), topHorizontalLayoutGroup.RectTransform),
345  iconStyleDefined ? iconStyle : "GUIButtonInfo", scaleToFit: true);
346  if (!iconStyleDefined)
347  {
348  Icon.Color = Color.Orange;
349  }
350  iconMaxHeight = (int)(Icon.Style.GetDefaultSprite()?.size.Y ?? GUI.yScale * 40);
351  }
352 
353  iconMaxHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
354  int iconMinHeight = Math.Min((int)(GUI.yScale * 40), iconMaxHeight);
355  Icon.RectTransform.MinSize = new Point(Icon.Rect.Width, iconMinHeight);
356  Icon.RectTransform.MaxSize = new Point(Icon.Rect.Width, iconMaxHeight);
357 
358  Content = new GUILayoutGroup(new RectTransform(new Vector2(Icon != null ? 0.85f : 1.0f, 1.0f), topHorizontalLayoutGroup.RectTransform))
359  {
360  AbsoluteSpacing = absoluteSpacing.Y,
361  };
362 
363  var bottomContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.3f), verticalLayoutGroup.RectTransform), style: null)
364  {
365  CanBeFocused = true
366  };
367 
368  var tickBoxLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.67f, 1.0f), bottomContainer.RectTransform, anchor: Anchor.CenterLeft))
369  {
370  CanBeFocused = true,
371  Stretch = true
372  };
373  Vector2 tickBoxRelativeSize = new Vector2(1.0f, 0.5f);
374  var dontShowAgainTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
375  TextManager.Get("hintmessagebox.dontshowagain"))
376  {
377  ToolTip = TextManager.Get("hintmessagebox.dontshowagaintooltip"),
378  UserData = "dontshowagain"
379  };
380  var disableHintsTickBox = new GUITickBox(new RectTransform(tickBoxRelativeSize, tickBoxLayoutGroup.RectTransform),
381  TextManager.Get("hintmessagebox.disablehints"))
382  {
383  ToolTip = TextManager.Get("hintmessagebox.disablehintstooltip"),
384  UserData = "disablehints"
385  };
386 
387  Buttons = new List<GUIButton>(1)
388  {
389  new GUIButton(new RectTransform(new Vector2(0.33f, 1.0f), bottomContainer.RectTransform, Anchor.CenterRight),
390  text: TextManager.Get("hintmessagebox.dismiss"), style: "GUIButtonSmall")
391  {
392  OnClicked = Close
393  }
394  };
395 
396  Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
397  GUIStyle.Apply(Header, "", this);
398  Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
399 
400  if (!text.IsNullOrWhiteSpace())
401  {
402  Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
403  GUIStyle.Apply(Text, "", this);
406  new Point(Text.Rect.Width, Text.Rect.Height);
408  if (headerText.IsNullOrWhiteSpace())
409  {
410  Header.RectTransform.Parent = null;
411  Content.ChildAnchor = Anchor.Center;
412  }
413  }
414 
415  if (height == 0)
416  {
417  height = absoluteSpacing.Y;
418  int upperContainerHeight = absoluteSpacing.Y;
419  if (Header.Rect.Height > 0) { upperContainerHeight += Header.Rect.Height + Content.AbsoluteSpacing; }
420  if (Text != null) { upperContainerHeight += Text.Rect.Height + Content.AbsoluteSpacing; }
421  upperContainerHeight = Math.Max(upperContainerHeight, Icon.Rect.Height);
422  height += upperContainerHeight;
423  height += absoluteSpacing.Y;
424  int bottomContainerHeight = dontShowAgainTickBox.Rect.Height + disableHintsTickBox.Rect.Height;
425  height += bottomContainerHeight;
426  height += absoluteSpacing.Y;
427  if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
428 
429  InnerFrame.RectTransform.NonScaledSize = new Point(InnerFrame.Rect.Width, height);
430  verticalLayoutGroup.RectTransform.NonScaledSize = GetVerticalLayoutGroupSize();
431  float upperContainerRelativeHeight = (float)upperContainerHeight / (upperContainerHeight + bottomContainerHeight);
432  topHorizontalLayoutGroup.RectTransform.RelativeSize = new Vector2(topHorizontalLayoutGroup.RectTransform.RelativeSize.X, upperContainerRelativeHeight);
433  bottomContainer.RectTransform.RelativeSize = new Vector2(bottomContainer.RectTransform.RelativeSize.X, 1.0f - upperContainerRelativeHeight);
434  verticalLayoutGroup.Recalculate();
435  topHorizontalLayoutGroup.Recalculate();
437  tickBoxLayoutGroup.Recalculate();
438  }
439 
440  InnerFrame.RectTransform.AbsoluteOffset = new Point(GUI.IntScale(64), -InnerFrame.Rect.Height);
441 
442  Point GetVerticalLayoutGroupSize()
443  {
444  return InnerFrame.Rect.Size - absoluteSpacing.Multiply(2);
445  }
446  }
447 
448  this.autoCloseCondition = autoCloseCondition;
449 
450  MessageBoxes.Add(this);
451  }
452 
456  public GUIMessageBox(Identifier hintIdentifier, LocalizedString text, Sprite icon) : this("", text, Array.Empty<LocalizedString>(), textAlignment: Alignment.CenterLeft, type: Type.Hint, icon: icon)
457  {
458  if (InnerFrame.FindChild("dontshowagain", recursive: true) is GUITickBox dontShowAgainTickBox)
459  {
460  dontShowAgainTickBox.OnSelected = HintManager.OnDontShowAgain;
461  dontShowAgainTickBox.UserData = hintIdentifier;
462  }
463  if (InnerFrame.FindChild("disablehints", recursive: true) is GUITickBox disableHintsTickBox)
464  {
465  disableHintsTickBox.OnSelected = HintManager.OnDisableHints;
466  disableHintsTickBox.UserData = hintIdentifier;
467  }
468  }
469 
470  private static Type[] messageBoxTypes;
471 
472  public static void AddActiveToGUIUpdateList()
473  {
474  messageBoxTypes ??= (Type[])Enum.GetValues(typeof(Type));
475 
476  foreach (var type in messageBoxTypes)
477  {
478  // Don't display hints when HUD is disabled
479  if (type == Type.Hint && GUI.DisableHUD) { continue; }
480 
481  for (int i = 0; i < MessageBoxes.Count; i++)
482  {
483  if (MessageBoxes[i] == null) { continue; }
484  if (MessageBoxes[i] is not GUIMessageBox messageBox)
485  {
486  if (type == Type.Default)
487  {
488  // Message box not of type GUIMessageBox is likely the round summary
489  MessageBoxes[i].AddToGUIUpdateList();
490  if (MessageBoxes[i].UserData is not RoundSummary) { break; }
491  }
492  continue;
493  }
494  if (messageBox.type != type) { continue; }
495  if (!messageBox.DisplayInLoadingScreens && GameMain.Instance.LoadingScreenOpen)
496  {
497  continue;
498  }
499 
500  // These are handled separately in GUI.HandlePersistingElements()
501  if (messageBox.DrawOnTop) { continue; }
502 
503  messageBox.AddToGUIUpdateList();
504  break;
505  }
506  }
507  }
508 
509  public void SetBackgroundIcon(Sprite icon)
510  {
511  if (icon == null) { return; }
512  if (icon == BackgroundIcon?.Sprite) { return; }
513  GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
514  {
515  IgnoreLayoutGroups = true,
516  Color = Color.Transparent
517  };
518 
519  if (newBackgroundIcon != null)
520  {
521  RemoveChild(newBackgroundIcon);
522  newBackgroundIcon = null;
523  }
524  newBackgroundIcon = newIcon;
525  }
526 
527  protected override void Update(float deltaTime)
528  {
529  if (PlayerInput.KeyHit(Keys.Enter))
530  {
531  OnEnterPressed?.Invoke();
532  }
533 
534  if (Draggable)
535  {
536  GUIComponent parent = GUI.MouseOn?.Parent?.Parent;
537  if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton || GUI.MouseOn is GUIColorPicker || GUI.MouseOn is GUITextBox || parent is GUITextBox))
538  {
539  GUI.MouseCursor = CursorState.Move;
541  {
543  }
544  }
545 
546  if (PlayerInput.PrimaryMouseButtonHeld() && DraggingPosition != Vector2.Zero)
547  {
548  GUI.MouseCursor = CursorState.Dragging;
550  }
551  else
552  {
553  DraggingPosition = Vector2.Zero;
554  }
555  }
556 
557  if (IsAnimated)
558  {
559  Vector2 initialPos, defaultPos, endPos;
560  if (type == Type.InGame || type == Type.Tutorial)
561  {
562  initialPos = new Vector2(0.0f, GameMain.GraphicsHeight);
563  defaultPos = new Vector2(0.0f, HUDLayoutSettings.InventoryAreaLower.Y - InnerFrame.Rect.Height - 20 * GUI.Scale);
564  endPos = new Vector2(GameMain.GraphicsWidth, defaultPos.Y);
565  }
566  else
567  {
568  initialPos = new Vector2(GUI.IntScale(64), -InnerFrame.Rect.Height);
569  defaultPos = new Vector2(initialPos.X, HUDLayoutSettings.ButtonAreaTop.Height + GUI.IntScale(64));
570  endPos = new Vector2(-InnerFrame.Rect.Width, defaultPos.Y);
571  }
572 
573  if (!closing)
574  {
575  Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
577  if (BackgroundIcon != null)
578  {
579  BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
580  if (!MathUtils.NearlyEqual(openState, 1.0f))
581  {
582  BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
583  }
584  }
585  if (!(Screen.Selected is RoundSummaryScreen) && !MessageBoxes.Any(mb => mb.UserData is RoundSummary))
586  {
587  openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
588  }
589 
590  if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
591  {
592  inGameCloseTimer += deltaTime;
593  }
594 
595  if (inGameCloseTimer >= inGameCloseTime)
596  {
597  Close();
598  }
599  else if (autoCloseCondition != null && autoCloseCondition())
600  {
601  Close();
603  {
604  InnerFrame.Flash(GUIStyle.Green);
605  }
606  }
607  }
608  else
609  {
610  openState += deltaTime * 2.0f;
611  Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
613  if (BackgroundIcon != null)
614  {
615  BackgroundIcon.Color *= 0.9f;
616  }
617  if (openState >= 2.0f)
618  {
619  Parent?.RemoveChild(this);
620  if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
621  }
622  }
623 
624  if (newBackgroundIcon != null)
625  {
626  if (!iconSwitching)
627  {
628  if (BackgroundIcon != null)
629  {
630  BackgroundIcon.Color *= 0.9f;
631  if (BackgroundIcon.Color.A == 0)
632  {
633  BackgroundIcon = null;
634  iconSwitching = true;
636  }
637  }
638  else
639  {
640  iconSwitching = true;
641  }
642  iconState = 0;
643  }
644  else
645  {
646  newBackgroundIcon.SetAsFirstChild();
647  newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int)(newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
648  newBackgroundIcon.Color = Color.Lerp(Color.Transparent, Color.White, iconState);
649  if (newBackgroundIcon.Color.A == 255)
650  {
651  BackgroundIcon = newBackgroundIcon;
653  newBackgroundIcon = null;
654  iconSwitching = false;
655  }
656 
657  iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
658  }
659  }
660  }
661  }
662 
663  public void Close()
664  {
665  if (IsAnimated)
666  {
667  closing = true;
668  }
669  else
670  {
671  Parent?.RemoveChild(this);
672  if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
673  }
674 
675  Closed = true;
676  }
677 
678  public bool Close(GUIButton button, object obj)
679  {
680  RectTransform.Parent = null;
681  Close();
682  return true;
683  }
684 
685  public static void CloseAll()
686  {
687  MessageBoxes.Clear();
688  }
689 
690  public static void Close(Identifier tag)
691  {
692  foreach (var messageBox in MessageBoxes)
693  {
694  if (messageBox is GUIMessageBox mb && mb.Tag == tag)
695  {
696  mb.Close();
697  }
698  }
699  }
700 
701  public static void Close(string tag) => Close(tag.ToIdentifier());
702 
706  public void AddButton(RectTransform rectT, string text, GUIButton.OnClickedHandler onClick)
707  {
708  rectT.Parent = RectTransform;
709  Buttons.Add(new GUIButton(rectT, text) { OnClicked = onClick });
710  }
711 
712  public static GUIMessageBox CreateLoadingBox(LocalizedString text, (LocalizedString Label, Action<GUIMessageBox> Action)[] buttons = null, Vector2? relativeSize = null)
713  {
714  buttons ??= Array.Empty<(LocalizedString Label, Action<GUIMessageBox> Action)>();
715  var relativeSizeFallback = relativeSize ?? (0.7f, 0.5f);
716  var newMessageBox = new GUIMessageBox(
717  headerText: "",
718  text: "",
719  relativeSize: relativeSizeFallback,
720  buttons: buttons.Select(b => b.Label).ToArray());
721  newMessageBox.InnerFrame.RectTransform.ScaleBasis = ScaleBasis.BothHeight;
722 
723  for (int i = 0; i < buttons.Length; i++)
724  {
725  var capturedIndex = i;
726  newMessageBox.Buttons[i].OnClicked = (_, _) =>
727  {
728  buttons[capturedIndex].Action(newMessageBox);
729  return false;
730  };
731  }
732 
733  const float throbberSize = 0.25f;
734 
735  new GUITextBlock(
736  new RectTransform((0.9f, 0f), newMessageBox.InnerFrame.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = (0f, -throbberSize * 0.5f) },
737  text: text, textAlignment: Alignment.Center, wrap: true);
738 
739  // Throbber
740  new GUICustomComponent(
741  new RectTransform(Vector2.One * throbberSize, newMessageBox.InnerFrame.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.BothHeight),
742  onDraw: static (sb, component) =>
743  {
744  GUIStyle.GenericThrobber.Draw(
745  sb,
746  spriteIndex: (int)(Timing.TotalTime * 20f) % GUIStyle.GenericThrobber.FrameCount,
747  pos: component.Rect.Center.ToVector2(),
748  color: Color.White,
749  origin: GUIStyle.GenericThrobber.FrameSize.ToVector2() * 0.5f,
750  rotate: 0f,
751  scale: component.Rect.Size.ToVector2() / GUIStyle.GenericThrobber.FrameSize.ToVector2());
752  });
753 
754  MessageBoxes.Remove(newMessageBox);
755  MessageBoxes.Insert(0, newMessageBox);
756  return newMessageBox;
757  }
758  }
759 }
OnClickedHandler OnClicked
Definition: GUIButton.cs:16
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectInflate=null)
Definition: GUIButton.cs:239
delegate bool OnClickedHandler(GUIButton button, object obj)
virtual void RemoveChild(GUIComponent child)
Definition: GUIComponent.cs:87
virtual void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectInflate=null)
bool IsParentOf(GUIComponent component, bool recursive=true)
Definition: GUIComponent.cs:75
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
Definition: GUIComponent.cs:95
virtual RichString ToolTip
virtual Rectangle Rect
GUIComponentStyle Style
RectTransform RectTransform
GUIComponent that can be used to render custom content on the UI
GUIFrame(RectTransform rectT, string style="", Color? color=null)
Definition: GUIFrame.cs:11
Sprite?? Sprite
Definition: GUIImage.cs:84
static readonly List< GUIComponent > MessageBoxes
static void AddActiveToGUIUpdateList()
void SetBackgroundIcon(Sprite icon)
GUIMessageBox(Identifier hintIdentifier, LocalizedString text, Sprite icon)
Use to create a message box of Hint type
bool DrawOnTop
If enabled, the box is always drawn in front of all other elements.
List< GUIButton > Buttons
bool Close(GUIButton button, object obj)
static void Close(Identifier tag)
static void Close(string tag)
bool AutoClose
Close the message box automatically after enough time has passed (inGameCloseTime)
void AddButton(RectTransform rectT, string text, GUIButton.OnClickedHandler onClick)
Parent does not matter. It's overridden.
GUILayoutGroup Content
static GUIMessageBox CreateLoadingBox(LocalizedString text,(LocalizedString Label, Action< GUIMessageBox > Action)[] buttons=null, Vector2? relativeSize=null)
static GUIComponent VisibleBox
override void Update(float deltaTime)
GUIMessageBox(LocalizedString headerText, LocalizedString text, Vector2? relativeSize=null, Point? minSize=null, Type type=Type.Default)
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)
static void AutoScaleAndNormalize(params GUITextBlock[] textBlocks)
Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text with...
static int GraphicsWidth
Definition: GameMain.cs:162
static int GraphicsHeight
Definition: GameMain.cs:168
static GameMain Instance
Definition: GameMain.cs:144
Defines a point in the event that GoTo actions can jump to.
Definition: Label.cs:7
Point ScreenSpaceOffset
Screen space offset. From top left corner. In pixels.
Point AbsoluteOffset
Absolute in pixels but relative to the anchor point. Calculated away from the anchor point,...
Vector2 RelativeSize
Relative to the parent rect.
RectTransform?? Parent
Point?? MinSize
Min size in pixels. Does not affect scaling.
Point NonScaledSize
Size before scale multiplications.
Point?? MaxSize
Max size in pixels. Does not affect scaling.
bool IsFixedSize
If false, the element will resize if the parent is resized (with the children). If true,...
Highlights an UI element of some kind. Generally used in tutorials.
CursorState
Definition: GUI.cs:40