3 using System.Collections.Generic;
6 using Microsoft.Xna.Framework;
20 : this(TextManager.Get(label).Fallback(label), isEnabled, onSelected) { }
23 : this(TextManager.Get(labelTag), isEnabled, onSelected) { }
42 internal class GUIContextMenu : GUIComponent
44 public static GUIContextMenu? CurrentContextMenu;
46 private readonly Dictionary<ContextMenuOption, GUITextBlock> Options =
new Dictionary<ContextMenuOption, GUITextBlock>();
47 private GUIContextMenu? SubMenu;
48 public readonly GUITextBlock? HeaderLabel;
49 public GUITextBlock? ParentOption;
59 public GUIContextMenu(Vector2? position, LocalizedString header,
string style, params ContextMenuOption[] options) : base(style, new
RectTransform(Point.Zero, GUI.Canvas))
61 Vector2 pos = position ?? PlayerInput.MousePosition;
62 GUIFont headerFont = GUIStyle.SubHeadingFont;
63 GUIFont font = GUIStyle.SmallFont;
64 Vector4 padding =
new Vector4(4), headerPadding =
new Vector4(8);
65 int horizontalPadding = (int)(padding.X + padding.Z), verticalPadding = (int)(padding.Y + padding.W);
66 bool hasHeader = !header.IsNullOrWhiteSpace();
72 Dictionary<ContextMenuOption, Vector2> optionsAndSizes =
new Dictionary<ContextMenuOption, Vector2>();
75 Point estimatedSize =
new Point(horizontalPadding, verticalPadding);
79 InflateSize(ref estimatedSize, header, headerFont!);
82 foreach (ContextMenuOption option
in options)
84 Vector2 optionSize = InflateSize(ref estimatedSize, option.Label, font!);
85 optionsAndSizes.Add(option, optionSize);
89 estimatedSize = estimatedSize.Multiply(1.2f);
98 GUILayoutGroup background =
new GUILayoutGroup(
new RectTransform(Vector2.One, RectTransform,
Anchor.Center))
103 Point listSize = estimatedSize;
106 Point sz = Point.Zero;
107 InflateSize(ref sz, header, headerFont!);
109 HeaderLabel =
new GUITextBlock(
new RectTransform(sz, background.RectTransform), header, font: headerFont) { Padding = headerPadding };
112 GUIListBox optionList =
new GUIListBox(
new RectTransform(listSize, background.RectTransform), style:
null)
114 AutoHideScrollBar =
false,
115 ScrollBarVisible =
false,
116 Padding = hasHeader ?
new Vector4(4, 0, 4, 4) : padding,
120 foreach (var (option, size) in optionsAndSizes)
122 GUITextBlock optionElement =
new GUITextBlock(
new RectTransform(size.ToPoint(), optionList.Content.RectTransform), option.Label, font: font)
127 Options.Add(option, optionElement);
129 if (!option.Tooltip.IsNullOrWhiteSpace() && optionElement.Enabled)
131 optionElement.ToolTip = option.Tooltip;
135 if (option.OnSelected ==
null)
137 optionElement.TextAlignment = Alignment.BottomLeft;
138 optionElement.TextColor = optionElement.DisabledTextColor = GUIStyle.Green;
140 else if (!option.IsEnabled)
142 optionElement.TextColor *= 0.5f;
150 List<GUIComponent> children = optionList.Content.Children.ToList();
153 foreach (GUITextBlock block
in children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
155 bool isLabel = block.UserData is ContextMenuOption option && option.OnSelected ==
null;
156 block.RectTransform.NonScaledSize =
new Point(
157 (
int)(block.TextSize.X + (block.Padding.X + block.Padding.Z)),
158 (
int)Math.Max(block.TextSize.Y * 1.2f, 18 * GUI.Scale));
161 int largestWidth = children.Max(c => c.Rect.Width + horizontalPadding);
164 if (HeaderLabel !=
null)
167 headerTransform.MinSize =
new Point((
int)(HeaderLabel.TextSize.X + (headerPadding.X + headerPadding.Z)), headerTransform.NonScaledSize.Y);
168 if (largestWidth < headerTransform.MinSize.X)
170 largestWidth = headerTransform.MinSize.X;
175 foreach (GUIComponent c
in children)
177 c.RectTransform.MinSize =
new Point(largestWidth, c.Rect.Height);
181 Point newSize =
new Point(largestWidth, children.Sum(c => c.Rect.Height) + verticalPadding);
184 optionList.RectTransform.NonScaledSize = newSize;
199 background.Recalculate();
201 optionList.OnSelected = OnSelected;
204 public static GUIContextMenu CreateContextMenu(params ContextMenuOption[] options) => CreateContextMenu(PlayerInput.MousePosition,
string.Empty,
null, options);
206 public static GUIContextMenu CreateContextMenu(Vector2? pos, LocalizedString header, Color? headerColor, params ContextMenuOption[] options)
208 GUIContextMenu menu =
new GUIContextMenu(pos,header,
"GUIToolTip", options);
209 if (headerColor !=
null)
211 menu.HeaderLabel?.OverrideTextColor(headerColor.Value);
213 CurrentContextMenu = menu;
217 private bool OnSelected(GUIComponent _,
object data)
219 if (data is ContextMenuOption option && option.IsEnabled)
221 CurrentContextMenu =
null;
236 private Vector2 InflateSize(ref Point size, LocalizedString label, ScalableFont font)
238 Vector2 textSize = font.MeasureString(label);
239 size.X = Math.Max((
int)Math.Ceiling(textSize.X), size.X);
240 size.Y += (int)Math.Ceiling(textSize.Y);
244 protected override void Update(
float deltaTime)
246 base.Update(deltaTime);
249 if (ParentOption !=
null)
254 if (SubMenu !=
null && !SubMenu.IsMouseOver())
260 foreach (var (option, textBlock) in Options)
263 if (GUI.MouseOn != textBlock) {
continue; }
264 if (option.IsEnabled && option.SubOptions is { } subOptions && subOptions.Any())
266 Vector2 subMenuPos =
new Vector2(textBlock.MouseRect.Right + 4, textBlock.MouseRect.Y);
267 SubMenu =
new GUIContextMenu(subMenuPos,
"",
"GUIToolTip", subOptions)
269 ParentOption = textBlock
279 private bool IsMouseOver()
282 expandedRect.Inflate(20, 20);
284 bool isMouseOn = expandedRect.Contains(PlayerInput.MousePosition);
286 if (ParentOption !=
null)
288 isMouseOn |= GUI.MouseOn == ParentOption;
292 if (!isMouseOn && SubMenu !=
null)
294 isMouseOn = SubMenu.IsMouseOver();
300 public override void AddToGUIUpdateList(
bool ignoreChildren =
false,
int order = 0)
302 base.AddToGUIUpdateList(ignoreChildren, order);
303 SubMenu?.AddToGUIUpdateList(order: 2);
306 public static void AddActiveToGUIUpdateList()
308 if (CurrentContextMenu !=
null && !CurrentContextMenu.IsMouseOver())
310 CurrentContextMenu =
null;
313 CurrentContextMenu?.AddToGUIUpdateList(order: 2);
virtual bool PlaySoundOnSelect
RectTransform RectTransform
Defines a point in the event that GoTo actions can jump to.