Client LuaCsForBarotrauma
GUIDropDown.cs
1 using EventInput;
2 using Microsoft.Xna.Framework;
3 using Microsoft.Xna.Framework.Graphics;
4 using Microsoft.Xna.Framework.Input;
5 using System.Collections.Generic;
6 using System.Linq;
7 
8 namespace Barotrauma
9 {
11  {
12  public delegate bool OnSelectedHandler(GUIComponent selected, object obj = null);
15 
16  private readonly GUIButton button;
17  private readonly GUIImage icon;
18  private readonly GUIListBox listBox;
19 
20  private RectTransform currentHighestParent;
21  private List<RectTransform> parentHierarchy = new List<RectTransform>();
22 
23  private readonly bool selectMultiple;
24 
25  public bool Dropped { get; set; }
26 
27  public bool AllowNonText { get; set; }
28 
29  public object SelectedItemData
30  {
31  get
32  {
33  if (listBox.SelectedComponent == null) return null;
34  return listBox.SelectedComponent.UserData;
35  }
36  }
37 
38  public override bool Enabled
39  {
40  get { return listBox.Enabled; }
41  set { listBox.Enabled = value; }
42  }
43 
44  public bool ButtonEnabled
45  {
46  get { return button.Enabled; }
47  set
48  {
49  button.Enabled = value;
50  if (icon != null) { icon.Enabled = value; }
51  }
52  }
53 
55  {
56  get { return listBox.SelectedComponent; }
57  }
58 
59  public override bool Selected
60  {
61  get
62  {
63  return Dropped;
64  }
65  set
66  {
67  Dropped = value;
68  }
69  }
70 
72  {
73  get { return listBox; }
74  }
75 
76  public object SelectedData
77  {
78  get
79  {
80  return listBox.SelectedComponent?.UserData;
81  }
82  }
83 
84  public int SelectedIndex
85  {
86  get
87  {
88  if (listBox.SelectedComponent == null) return -1;
89  return listBox.Content.GetChildIndex(listBox.SelectedComponent);
90  }
91  }
92 
93  public Color ButtonTextColor
94  {
95  get { return button.TextColor; }
96  set { button.TextColor = value; }
97  }
98 
99  public override GUIFont Font
100  {
101  get { return button?.Font ?? base.Font; }
102  set
103  {
104  if (button != null) { button.Font = value; }
105  }
106  }
107 
108  public void ReceiveTextInput(char inputChar)
109  {
110  GUI.KeyboardDispatcher.Subscriber = null;
111  }
112  public void ReceiveTextInput(string text) { }
113  public void ReceiveCommandInput(char command) { }
114  public void ReceiveEditingInput(string text, int start, int length) { }
115 
116  public void ReceiveSpecialInput(Keys key)
117  {
118  switch (key)
119  {
120  case Keys.Up:
121  case Keys.Down:
122  listBox.ReceiveSpecialInput(key);
123  GUI.KeyboardDispatcher.Subscriber = this;
124  break;
125  default:
126  GUI.KeyboardDispatcher.Subscriber = null;
127  break;
128  }
129  }
130 
131  private readonly List<object> selectedDataMultiple = new List<object>();
132  public IEnumerable<object> SelectedDataMultiple
133  {
134  get { return selectedDataMultiple; }
135  }
136 
137  private readonly List<int> selectedIndexMultiple = new List<int>();
138  public IEnumerable<int> SelectedIndexMultiple
139  {
140  get { return selectedIndexMultiple; }
141  }
142 
143  public bool MustSelectAtLeastOne;
144 
146  {
147  get { return button.Text; }
148  set { button.Text = value; }
149  }
150 
151  public override RichString ToolTip
152  {
153  get
154  {
155  return base.ToolTip;
156  }
157  set
158  {
159  base.ToolTip = value;
160  button.ToolTip = value;
161  listBox.ToolTip = value;
162  }
163  }
164 
165  public GUIImage DropDownIcon => icon;
166 
167  public Vector4 Padding => button.TextBlock.Padding;
168 
169  public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
170  {
171  text ??= LocalizedString.EmptyString;
172 
173  HoverCursor = CursorState.Hand;
174  CanBeFocused = true;
175 
176  this.selectMultiple = selectMultiple;
177 
178  button = new GUIButton(new RectTransform(Vector2.One, rectT), text, textAlignment, style: "GUIDropDown")
179  {
180  OnClicked = OnClicked,
181  TextBlock = { OverflowClip = true }
182  };
183  GUIStyle.Apply(button, "", this);
184  button.TextBlock.SetTextPos();
185 
186  Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
187  Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter;
188  listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
189  { IsFixedSize = false }, style: null)
190  {
191  Enabled = !selectMultiple,
192  PlaySoundOnSelect = true,
193  };
194  if (!selectMultiple) { listBox.OnSelected = SelectItem; }
195  GUIStyle.Apply(listBox, "GUIListBox", this);
196  GUIStyle.Apply(listBox.ContentBackground, "GUIListBox", this);
197 
198  if (button.Style.ChildStyles.ContainsKey("dropdownicon".ToIdentifier()))
199  {
200  icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), button.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, null, scaleToFit: true);
201  icon.ApplyStyle(button.Style.ChildStyles["dropdownicon".ToIdentifier()]);
202  }
203 
204  currentHighestParent = FindHighestParent();
205  currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
206  rectT.ParentChanged += _ => RefreshListBoxParent();
207  }
208 
209 
215  private RectTransform FindHighestParent()
216  {
217  parentHierarchy.Clear();
218 
219  //collect entire parent hierarchy to a list
220  parentHierarchy = new List<RectTransform>() { RectTransform.Parent };
221  RectTransform parent = parentHierarchy.Last();
222  while (parent?.Parent != null)
223  {
224  parentHierarchy.Add(parent.Parent);
225  parent = parent.Parent;
226  }
227 
228  //find the highest parent that has a guicomponent with a style
229  //(and so should be rendered and not just some empty parent/root element used for constructing a layout)
230  for (int i = parentHierarchy.Count - 1; i > 0; i--)
231  {
232  if (parentHierarchy[i] is GUICanvas ||
233  parentHierarchy[i].GUIComponent == null ||
234  parentHierarchy[i].GUIComponent.Style == null ||
235  parentHierarchy[i].GUIComponent == Screen.Selected?.Frame)
236  {
237  parentHierarchy.RemoveAt(i);
238  }
239  else
240  {
241  break;
242  }
243  }
244  return parentHierarchy.Last();
245  }
246 
247  public GUIComponent AddItem(LocalizedString text, object userData = null, LocalizedString toolTip = null, Color? color = null, Color? textColor = null)
248  {
249  toolTip ??= "";
250  if (selectMultiple)
251  {
252  var frame = new GUIFrame(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, style: "ListBoxElement", color: color)
253  {
254  UserData = userData,
255  ToolTip = toolTip
256  };
257  new GUITickBox(new RectTransform(new Vector2(1.0f, 0.8f), frame.RectTransform, anchor: Anchor.CenterLeft) { MaxSize = new Point(int.MaxValue, (int)(button.Rect.Height * 0.8f)) }, text)
258  {
259  UserData = userData,
260  ToolTip = toolTip,
261  OnSelected = (GUITickBox tb) =>
262  {
263  if (MustSelectAtLeastOne && selectedIndexMultiple.Count <= 1 && !tb.Selected)
264  {
265  tb.Selected = true;
266  return false;
267  }
268 
269  List<LocalizedString> texts = new List<LocalizedString>();
270  selectedDataMultiple.Clear();
271  selectedIndexMultiple.Clear();
272  int i = 0;
273  foreach (GUIComponent child in ListBox.Content.Children)
274  {
275  var tickBox = child.GetChild<GUITickBox>();
276  if (tickBox is { Selected: true })
277  {
278  selectedDataMultiple.Add(child.UserData);
279  selectedIndexMultiple.Add(i);
280  texts.Add(tickBox.Text);
281  }
282  i++;
283  }
284  button.Text = LocalizedString.Join(", ", texts);
285  // TODO: The callback is called at least twice, remove this?
286  OnSelected?.Invoke(tb.Parent, tb.Parent.UserData);
287  return true;
288  }
289  };
290  return frame;
291  }
292  else
293  {
294  return new GUITextBlock(new RectTransform(new Point(button.Rect.Width, button.Rect.Height), listBox.Content.RectTransform) { IsFixedSize = false }, text, style: "ListBoxElement", color: color, textColor: textColor)
295  {
296  UserData = userData,
297  ToolTip = toolTip
298  };
299  }
300  }
301 
302  public override void ClearChildren()
303  {
304  listBox.ClearChildren();
305  }
306 
307  public IEnumerable<GUIComponent> GetChildren()
308  {
309  return listBox.Content.Children;
310  }
311 
312  private bool SelectItem(GUIComponent component, object obj)
313  {
314  if (selectMultiple)
315  {
316  foreach (GUIComponent child in ListBox.Content.Children)
317  {
318  var tickBox = child.GetChild<GUITickBox>();
319  if (Equals(obj, child.UserData)) { tickBox.Selected = true; }
320  }
321  }
322  else
323  {
324  if (component is not GUITextBlock textBlock)
325  {
326  textBlock = component.GetChild<GUITextBlock>();
327  if (textBlock is null && !AllowNonText) { return false; }
328  }
329  button.Text = textBlock?.Text ?? "";
330  }
331  Dropped = false;
332  // TODO: OnSelected can be called multiple times and when it shouldn't be called -> turn into an event so that nobody else can call it.
333  OnSelected?.Invoke(component, component.UserData);
334  return true;
335  }
336 
337  public void SelectItem(object userData)
338  {
339  if (selectMultiple)
340  {
341  SelectItem(listBox.Content.FindChild(userData), userData);
342  }
343  else
344  {
345  listBox.Select(userData);
346  }
347  }
348 
349  public void Select(int index)
350  {
351  if (selectMultiple)
352  {
353  var child = listBox.Content.GetChild(index);
354  if (child != null)
355  {
356  SelectItem(null, child.UserData);
357  }
358  }
359  else
360  {
361  listBox.Select(index);
362  }
363  }
364 
365  private bool wasOpened;
366 
367  private bool OnClicked(GUIComponent component, object obj)
368  {
369  if (wasOpened) return false;
370 
371  wasOpened = true;
372  Dropped = !Dropped;
373  if (Dropped && Enabled)
374  {
375  OnDropped?.Invoke(this, UserData);
376  listBox.UpdateScrollBarSize();
377  listBox.UpdateDimensions();
378 
379  GUI.KeyboardDispatcher.Subscriber = this;
380  }
381  else if (GUI.KeyboardDispatcher.Subscriber == this)
382  {
383  GUI.KeyboardDispatcher.Subscriber = null;
384  }
385  return true;
386  }
387 
388  public void RefreshListBoxParent()
389  {
390  currentHighestParent.GUIComponent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
391  if (RectTransform.Parent == null) { return; }
392 
393  currentHighestParent = FindHighestParent();
394  currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
395  }
396 
397  private void AddListBoxToGUIUpdateList(GUIComponent parent)
398  {
399  //the parent is not our parent anymore :(
400  //can happen when subscribed to a parent higher in the hierarchy (instead of the direct parent),
401  //and somewhere between this component and the higher parent a component was removed
402  for (int i = 1; i < parentHierarchy.Count; i++)
403  {
404  if (parentHierarchy[i].IsParentOf(parentHierarchy[i - 1], recursive: false))
405  {
406  continue;
407  }
408 
409  parent.OnAddedToGUIUpdateList -= AddListBoxToGUIUpdateList;
410  return;
411  }
412 
413  if (Dropped)
414  {
415  listBox.AddToGUIUpdateList(false, 1);
416  }
417  }
418 
419  public override void DrawManually(SpriteBatch spriteBatch, bool alsoChildren = false, bool recursive = true)
420  {
421  if (!Visible) return;
422 
423  AutoDraw = false;
424  Draw(spriteBatch);
425  if (alsoChildren)
426  {
427  button.DrawManually(spriteBatch, alsoChildren, recursive);
428  }
429  }
430 
431  public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
432  {
433  base.AddToGUIUpdateList(true, order);
434  if (!ignoreChildren)
435  {
436  button.AddToGUIUpdateList(false, order);
437  }
438  }
439 
440  protected override void Update(float deltaTime)
441  {
442  if (!Visible) return;
443  wasOpened = false;
444  base.Update(deltaTime);
446  {
447  Rectangle listBoxRect = listBox.Rect;
448  if (!listBoxRect.Contains(PlayerInput.MousePosition) && !button.Rect.Contains(PlayerInput.MousePosition))
449  {
450  Dropped = false;
451  if (GUI.KeyboardDispatcher.Subscriber == this)
452  {
453  GUI.KeyboardDispatcher.Subscriber = null;
454  }
455  }
456  }
457  }
458  }
459 }
GUIComponent(string style, RectTransform rectT)
This is the new constructor.
GUIComponent GetChild(int index)
Definition: GUIComponent.cs:54
bool IsParentOf(GUIComponent component, bool recursive=true)
Definition: GUIComponent.cs:75
virtual bool PlaySoundOnSelect
Action< GUIComponent > OnAddedToGUIUpdateList
virtual void Draw(SpriteBatch spriteBatch)
virtual Rectangle Rect
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
override void Update(float deltaTime)
Definition: GUIDropDown.cs:440
void SelectItem(object userData)
Definition: GUIDropDown.cs:337
GUIComponent AddItem(LocalizedString text, object userData=null, LocalizedString toolTip=null, Color? color=null, Color? textColor=null)
Definition: GUIDropDown.cs:247
void ReceiveTextInput(string text)
Definition: GUIDropDown.cs:112
GUIDropDown(RectTransform rectT, LocalizedString text=null, int elementCount=4, string style="", bool selectMultiple=false, bool dropAbove=false, Alignment textAlignment=Alignment.CenterLeft)
Definition: GUIDropDown.cs:169
void ReceiveSpecialInput(Keys key)
Definition: GUIDropDown.cs:116
void ReceiveTextInput(char inputChar)
Definition: GUIDropDown.cs:108
IEnumerable< GUIComponent > GetChildren()
Definition: GUIDropDown.cs:307
OnSelectedHandler OnSelected
Definition: GUIDropDown.cs:13
IEnumerable< int > SelectedIndexMultiple
Definition: GUIDropDown.cs:139
override GUIFont??? Font
Definition: GUIDropDown.cs:100
void ReceiveEditingInput(string text, int start, int length)
Definition: GUIDropDown.cs:114
override bool Selected
Definition: GUIDropDown.cs:60
void Select(int index)
Definition: GUIDropDown.cs:349
override bool Enabled
Definition: GUIDropDown.cs:39
OnSelectedHandler OnDropped
Definition: GUIDropDown.cs:14
override void DrawManually(SpriteBatch spriteBatch, bool alsoChildren=false, bool recursive=true)
By default, all the gui elements are drawn automatically in the same order they appear on the update ...
Definition: GUIDropDown.cs:419
override void ClearChildren()
Definition: GUIDropDown.cs:302
override void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
Definition: GUIDropDown.cs:431
override RichString ToolTip
Definition: GUIDropDown.cs:152
delegate bool OnSelectedHandler(GUIComponent selected, object obj=null)
IEnumerable< object > SelectedDataMultiple
Definition: GUIDropDown.cs:133
GUIComponent SelectedComponent
Definition: GUIDropDown.cs:55
LocalizedString Text
Definition: GUIDropDown.cs:146
void ReceiveCommandInput(char command)
Definition: GUIDropDown.cs:113
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
LocalizedString Text
Definition: GUITickBox.cs:109
override bool Selected
Definition: GUITickBox.cs:18
static readonly RawLString EmptyString
static LocalizedString Join(string separator, params LocalizedString[] subStrs)
GUIComponent GUIComponent
Should be assigned only by GUIComponent. Note that RectTransform is created first and the GUIComponen...
RectTransform?? Parent
Action< RectTransform > ParentChanged
CursorState
Definition: GUI.cs:40