Client LuaCsForBarotrauma
SpriteEditorScreen.cs
1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using Microsoft.Xna.Framework.Input;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Xml.Linq;
9 #if DEBUG
10 using System.IO;
11 #else
12 using Barotrauma.IO;
13 #endif
14 
15 namespace Barotrauma
16 {
18  {
19  private GUIListBox textureList, spriteList;
20 
21  private GUIFrame topPanel;
22  private GUIFrame leftPanel;
23  private GUIFrame rightPanel;
24  private GUIFrame bottomPanel;
25  private GUIFrame backgroundColorPanel;
26 
27  private bool drawGrid, snapToGrid;
28 
29  private GUIFrame topPanelContents;
30  private GUITextBlock texturePathText;
31  private GUITextBlock xmlPathText;
32  private GUIScrollBar zoomBar;
33  private readonly List<Sprite> selectedSprites = new List<Sprite>();
34  private readonly List<Sprite> dirtySprites = new List<Sprite>();
35  private Texture2D SelectedTexture => lastSprite?.Texture;
36  private Sprite lastSprite;
37  private string selectedTexturePath;
38 
39  private Rectangle textureRect;
40  private float zoom = 1;
41  private const float MinZoom = 0.25f, MaxZoom = 10.0f;
42 
43  private GUITextBox filterSpritesBox;
44  private GUITextBlock filterSpritesLabel;
45  private GUITextBox filterTexturesBox;
46  private GUITextBlock filterTexturesLabel;
47 
48  private LocalizedString originLabel, positionLabel, sizeLabel;
49 
50  private bool editBackgroundColor;
51  private Color backgroundColor = new Color(0.051f, 0.149f, 0.271f, 1.0f);
52 
53  private bool ControlDown => PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl);
54 
55  private readonly Camera cam;
56  public override Camera Cam
57  {
58  get { return cam; }
59  }
60 
62  {
63  get { return topPanel; }
64  }
65 
67  {
68  cam = new Camera();
70  CreateUI();
71  }
72 
73  #region Initialization
74  private void CreateUI()
75  {
76  originLabel = TextManager.Get("charactereditor.origin");
77  positionLabel = TextManager.GetWithVariable("charactereditor.position", "[coordinates]", string.Empty);
78  sizeLabel = TextManager.Get("charactereditor.size");
79 
80  topPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.15f), Frame.RectTransform) { MinSize = new Point(0, 60) }, "GUIFrameTop");
81  topPanelContents = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.8f), topPanel.RectTransform, Anchor.Center), style: null);
82 
83  new GUIButton(new RectTransform(new Vector2(0.14f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
84  {
85  RelativeOffset = new Vector2(0, 0.1f)
86  }, TextManager.Get("spriteeditor.reloadtexture"))
87  {
88  OnClicked = (button, userData) =>
89  {
90  var selected = selectedSprites.ToList();
91  Sprite firstSelected = selected.First();
92  selected.ForEach(s => s.ReloadTexture());
93  RefreshLists();
94  textureList.Select(firstSelected.FullPath, autoScroll: GUIListBox.AutoScroll.Disabled);
95  selected.ForEachMod(s => spriteList.Select(s, autoScroll: GUIListBox.AutoScroll.Disabled));
96  texturePathText.Text = TextManager.GetWithVariable("spriteeditor.texturesreloaded", "[filepath]", firstSelected.FilePath.Value);
97  texturePathText.TextColor = GUIStyle.Green;
98  return true;
99  }
100  };
101  new GUIButton(new RectTransform(new Vector2(0.14f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
102  {
103  RelativeOffset = new Vector2(0, 0.1f)
104  }, TextManager.Get("spriteeditor.resetchanges"))
105  {
106  OnClicked = (button, userData) =>
107  {
108  if (SelectedTexture == null) { return false; }
109  foreach (Sprite sprite in loadedSprites)
110  {
111  if (sprite.FullPath != selectedTexturePath) { continue; }
112  var element = sprite.SourceElement;
113  if (element == null) { continue; }
114  // Not all sprites have a sourcerect defined, in which case we'll want to use the current source rect instead of an empty rect.
115  sprite.SourceRect = element.GetAttributeRect("sourcerect", sprite.SourceRect);
116  sprite.RelativeOrigin = element.GetAttributeVector2("origin", new Vector2(0.5f, 0.5f));
117  }
118  ResetWidgets();
119  xmlPathText.Text = TextManager.Get("spriteeditor.resetsuccessful");
120  xmlPathText.TextColor = GUIStyle.Green;
121  return true;
122  }
123  };
124  new GUIButton(new RectTransform(new Vector2(0.14f, 0.4f), topPanelContents.RectTransform, Anchor.TopLeft)
125  {
126  RelativeOffset = new Vector2(0.15f, 0.1f)
127  }, TextManager.Get("spriteeditor.saveselectedsprites"))
128  {
129  OnClicked = (button, userData) =>
130  {
131  return SaveSprites(selectedSprites);
132  }
133  };
134  new GUIButton(new RectTransform(new Vector2(0.14f, 0.4f), topPanelContents.RectTransform, Anchor.BottomLeft)
135  {
136  RelativeOffset = new Vector2(0.15f, 0.1f)
137  }, TextManager.Get("spriteeditor.saveallsprites"))
138  {
139  OnClicked = (button, userData) =>
140  {
141  return SaveSprites(loadedSprites);
142  }
143  };
144 
145  GUITextBlock.AutoScaleAndNormalize(topPanelContents.Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock));
146 
147  new GUITextBlock(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, TextManager.Get("spriteeditor.zoom"));
148  zoomBar = new GUIScrollBar(new RectTransform(new Vector2(0.2f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterRight)
149  {
150  RelativeOffset = new Vector2(0.05f, 0.3f)
151  }, style: "GUISlider", barSize: 0.1f)
152  {
153  BarScroll = GetBarScrollValue(),
154  Step = 0.01f,
155  OnMoved = (scrollBar, value) =>
156  {
157  zoom = MathHelper.Lerp(MinZoom, MaxZoom, value);
158  viewAreaOffset = Point.Zero;
159  return true;
160  }
161  };
162  var resetBtn = new GUIButton(new RectTransform(new Vector2(0.05f, 0.35f), topPanelContents.RectTransform, Anchor.TopCenter, Pivot.CenterLeft) { RelativeOffset = new Vector2(0.055f, 0.3f) }, TextManager.Get("spriteeditor.resetzoom"))
163  {
164  OnClicked = (box, data) =>
165  {
166  ResetZoom();
167  return true;
168  }
169  };
170  resetBtn.TextBlock.AutoScaleHorizontal = true;
171 
172  new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0, 0.3f) }, TextManager.Get("spriteeditor.showgrid"))
173  {
174  Selected = drawGrid,
175  OnSelected = (tickBox) =>
176  {
177  drawGrid = tickBox.Selected;
178  return true;
179  }
180  };
181  new GUITickBox(new RectTransform(new Vector2(0.2f, 0.2f), topPanelContents.RectTransform, Anchor.BottomCenter, Pivot.CenterRight) { RelativeOffset = new Vector2(0.17f, 0.3f) }, TextManager.Get("spriteeditor.snaptogrid"))
182  {
183  Selected = snapToGrid,
184  OnSelected = (tickBox) =>
185  {
186  snapToGrid = tickBox.Selected;
187  return true;
188  }
189  };
190 
191  texturePathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.BottomCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
192  xmlPathText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.4f), topPanelContents.RectTransform, Anchor.Center, Pivot.TopCenter) { RelativeOffset = new Vector2(0.4f, 0) }, "", Color.LightGray);
193 
194  leftPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomLeft)
195  { MinSize = new Point(150, 0) }, style: "GUIFrameLeft");
196  var paddedLeftPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), leftPanel.RectTransform, Anchor.Center))
197  { RelativeSpacing = 0.01f, Stretch = true };
198 
199  var filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
200  {
201  Stretch = true,
202  UserData = "filterarea"
203  };
204  filterTexturesLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.Font, textAlignment: Alignment.CenterLeft) { IgnoreLayoutGroups = true }; ;
205  filterTexturesBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUIStyle.Font, createClearButton: true);
206  filterArea.RectTransform.MinSize = filterTexturesBox.RectTransform.MinSize;
207  filterTexturesBox.OnTextChanged += (textBox, text) => { FilterTextures(text); return true; };
208 
209  textureList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedLeftPanel.RectTransform))
210  {
211  PlaySoundOnSelect = true,
212  OnSelected = (listBox, userData) =>
213  {
214  var newTexturePath = userData as string;
215  if (selectedTexturePath == null || selectedTexturePath != newTexturePath)
216  {
217  selectedTexturePath = newTexturePath;
218  ResetZoom();
219  spriteList.Select(loadedSprites.First(s => s.FilePath == selectedTexturePath), autoScroll: GUIListBox.AutoScroll.Disabled);
220  UpdateScrollBar(spriteList);
221  }
222  foreach (GUIComponent child in spriteList.Content.Children)
223  {
224  var textBlock = (GUITextBlock)child;
225  var sprite = (Sprite)textBlock.UserData;
226  textBlock.TextColor = new Color(textBlock.TextColor, sprite.FilePath == selectedTexturePath ? 1.0f : 0.4f);
227  if (sprite.FilePath == selectedTexturePath) { textBlock.Visible = true; }
228  }
229  texturePathText.TextColor = Color.LightGray;
230  topPanelContents.Visible = true;
231  return true;
232  }
233  };
234 
235  rightPanel = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f - topPanel.RectTransform.RelativeSize.Y), Frame.RectTransform, Anchor.BottomRight) { MinSize = new Point(150, 0) }, style: "GUIFrameRight");
236  var paddedRightPanel = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), rightPanel.RectTransform, Anchor.Center))
237  {
238  Stretch = true,
239  RelativeSpacing = 0.01f
240  };
241 
242  filterArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal: true)
243  {
244  Stretch = true,
245  UserData = "filterarea"
246  };
247  filterSpritesLabel = new GUITextBlock(new RectTransform(Vector2.One, filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUIStyle.Font, textAlignment: Alignment.CenterLeft) { IgnoreLayoutGroups = true };
248  filterSpritesBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUIStyle.Font, createClearButton: true);
249  filterArea.RectTransform.MinSize = filterSpritesBox.RectTransform.MinSize;
250  filterSpritesBox.OnTextChanged += (textBox, text) => { FilterSprites(text); return true; };
251 
252  spriteList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform))
253  {
254  PlaySoundOnSelect = true,
255  OnSelected = (listBox, userData) =>
256  {
257  if (userData is Sprite sprite)
258  {
259  SelectSprite(sprite);
260  return true;
261  }
262  return false;
263  }
264  };
265 
266  // Background color
267  bottomPanel = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.05f), Frame.RectTransform, Anchor.BottomCenter), style: null, color: Color.Black * 0.5f);
268  new GUITickBox(new RectTransform(new Vector2(0.2f, 0.5f), bottomPanel.RectTransform, Anchor.Center), TextManager.Get("charactereditor.editbackgroundcolor"))
269  {
270  Selected = editBackgroundColor,
271  OnSelected = box =>
272  {
273  editBackgroundColor = box.Selected;
274  return true;
275  }
276  };
277  backgroundColorPanel = new GUIFrame(new RectTransform(new Point(400, 80), Frame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0, 0.1f) }, style: null, color: Color.Black * 0.4f);
278  new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), backgroundColorPanel.RectTransform) { MinSize = new Point(80, 26) }, TextManager.Get("spriteeditor.backgroundcolor"), textColor: Color.WhiteSmoke);
279  var inputArea = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 1), backgroundColorPanel.RectTransform, Anchor.TopRight)
280  {
281  AbsoluteOffset = new Point(20, 0)
282  }, isHorizontal: true, childAnchor: Anchor.CenterRight)
283  {
284  Stretch = true,
285  RelativeSpacing = 0.01f
286  };
287  var fields = new GUIComponent[4];
288  LocalizedString[] colorComponentLabels = { TextManager.Get("spriteeditor.colorcomponentr"), TextManager.Get("spriteeditor.colorcomponentg"), TextManager.Get("spriteeditor.colorcomponentb") };
289  for (int i = 2; i >= 0; i--)
290  {
291  var element = new GUIFrame(new RectTransform(new Vector2(0.2f, 1), inputArea.RectTransform)
292  {
293  MinSize = new Point(40, 0),
294  MaxSize = new Point(100, 50)
295  }, style: null, color: Color.Black * 0.6f);
296  var colorLabel = new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), element.RectTransform, Anchor.CenterLeft), colorComponentLabels[i],
297  font: GUIStyle.SmallFont, textAlignment: Alignment.CenterLeft);
298  var numberInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1), element.RectTransform, Anchor.CenterRight), NumberType.Int)
299  {
300  Font = GUIStyle.SmallFont
301  };
302  numberInput.MinValueInt = 0;
303  numberInput.MaxValueInt = 255;
304  numberInput.Font = GUIStyle.SmallFont;
305  switch (i)
306  {
307  case 0:
308  colorLabel.TextColor = GUIStyle.Red;
309  numberInput.IntValue = backgroundColor.R;
310  numberInput.OnValueChanged += (numInput) => backgroundColor.R = (byte)(numInput.IntValue);
311  break;
312  case 1:
313  colorLabel.TextColor = GUIStyle.Green;
314  numberInput.IntValue = backgroundColor.G;
315  numberInput.OnValueChanged += (numInput) => backgroundColor.G = (byte)(numInput.IntValue);
316  break;
317  case 2:
318  colorLabel.TextColor = Color.DeepSkyBlue;
319  numberInput.IntValue = backgroundColor.B;
320  numberInput.OnValueChanged += (numInput) => backgroundColor.B = (byte)(numInput.IntValue);
321  break;
322  }
323  }
324  }
325 
326  private readonly HashSet<Sprite> loadedSprites = new HashSet<Sprite>();
327  private void LoadSprites()
328  {
329  loadedSprites.ForEach(s => s.Remove());
330  loadedSprites.Clear();
331  var contentPackages = ContentPackageManager.EnabledPackages.All.ToList();
332 
333 #if !DEBUG
334  var vanilla = GameMain.VanillaContent;
335  if (vanilla != null)
336  {
337  contentPackages.Remove(vanilla);
338  }
339 #endif
340  foreach (var contentPackage in contentPackages)
341  {
342  foreach (var file in contentPackage.Files)
343  {
344  if (file.Path.EndsWith(".xml"))
345  {
346  XDocument doc = XMLExtensions.TryLoadXml(file.Path);
347  if (doc != null)
348  {
349  LoadSprites(doc.Root.FromPackage(file.Path.ContentPackage));
350  }
351  }
352  }
353  }
354 
355  void LoadSprites(ContentXElement element)
356  {
357  string[] spriteElementNames = {
358  "Sprite",
359  "DeformableSprite",
360  "BackgroundSprite",
361  "BrokenSprite",
362  "ContainedSprite",
363  "InventoryIcon",
364  "Icon",
365  "VineSprite",
366  "LeafSprite",
367  "FlowerSprite",
368  "DecorativeSprite",
369  "BarrelSprite",
370  "RailSprite",
371  "SchematicSprite",
372  "WeldedSprite"
373  };
374 
375  foreach (string spriteElementName in spriteElementNames)
376  {
377  element.GetChildElements(spriteElementName).ForEach(s => CreateSprite(s));
378  }
379 
380  element.Elements().ForEach(e => LoadSprites(e));
381  }
382 
383  void CreateSprite(ContentXElement element)
384  {
385  //empty element, probably an item variant?
386  if (element.Attributes().None()) { return; }
387 
388  string spriteFolder = "";
389  ContentPath texturePath = null;
390 
391  if (element.GetAttribute("texture") != null)
392  {
393  texturePath = element.GetAttributeContentPath("texture");
394  }
395  else
396  {
397  if (element.Name.ToString().ToLower() == "vinesprite")
398  {
399  texturePath = element.Parent.GetAttributeContentPath("vineatlas");
400  }
401  }
402  if (texturePath.IsNullOrEmpty()) { return; }
403 
404  // TODO: parse and create?
405  if (texturePath.Value.Contains("[GENDER]") || texturePath.Value.Contains("[HEADID]") || texturePath.Value.Contains("[RACE]") || texturePath.Value.Contains("[VARIANT]")) { return; }
406  if (!texturePath.Value.Contains("/"))
407  {
408  var parsedPath = element.ParseContentPathFromUri();
409  spriteFolder = Path.GetDirectoryName(parsedPath);
410  }
411  // Uncomment if we do multiple passes -> there can be duplicates
412  //string identifier = Sprite.GetID(element);
413  //if (loadedSprites.None(s => s.ID == identifier))
414  //{
415  // loadedSprites.Add(new Sprite(element, spriteFolder));
416  //}
417  loadedSprites.Add(new Sprite(element, spriteFolder, texturePath.Value, lazyLoad: true));
418  }
419  }
420 
421  private bool SaveSprites(IEnumerable<Sprite> sprites)
422  {
423  if (SelectedTexture == null) { return false; }
424  if (sprites.None()) { return false; }
425  HashSet<XDocument> docsToSave = new HashSet<XDocument>();
426  foreach (Sprite sprite in sprites)
427  {
428  if (sprite.FullPath != selectedTexturePath) { continue; }
429  var element = sprite.SourceElement;
430  if (element == null) { continue; }
431  element.SetAttributeValue("sourcerect", XMLExtensions.RectToString(sprite.SourceRect));
432  element.SetAttributeValue("origin", XMLExtensions.Vector2ToString(sprite.RelativeOrigin));
433 
434  /*if (element.Attribute("slice") != null)
435  {
436  Rectangle slice = new Rectangle(
437  sprite.SourceRect.X + 5,
438  sprite.SourceRect.Y + 5,
439  sprite.SourceRect.Right - 5,
440  sprite.SourceRect.Bottom - 5);
441  element.SetAttributeValue("slice", XMLExtensions.RectToString(slice));
442  }*/
443  docsToSave.Add(element.Document);
444  }
445  xmlPathText.Text = TextManager.Get("spriteeditor.allchangessavedto");
446  foreach (XDocument doc in docsToSave)
447  {
448  string xmlPath = doc.ParseContentPathFromUri();
449  xmlPathText.Text += "\n" + xmlPath;
450 #if DEBUG
451  doc.Save(xmlPath);
452 #else
453  doc.SaveSafe(xmlPath);
454 #endif
455  }
456  xmlPathText.TextColor = GUIStyle.Green;
457  return true;
458  }
459 #endregion
460 
461  #region Public methods
462  public override void AddToGUIUpdateList()
463  {
464  leftPanel.AddToGUIUpdateList();
465  rightPanel.AddToGUIUpdateList();
466  topPanel.AddToGUIUpdateList();
467  bottomPanel.AddToGUIUpdateList();
468  if (editBackgroundColor)
469  {
470  backgroundColorPanel.AddToGUIUpdateList();
471  }
472  }
473 
474  public override void Update(double deltaTime)
475  {
476  base.Update(deltaTime);
477  Widget.EnableMultiSelect = ControlDown;
478  spriteList.SelectMultiple = Widget.EnableMultiSelect;
479  // Select rects with the mouse
480  if (Widget.SelectedWidgets.None() || Widget.EnableMultiSelect)
481  {
482  if (SelectedTexture != null && GUI.MouseOn == null)
483  {
484  foreach (Sprite sprite in loadedSprites)
485  {
486  if (sprite.FullPath != selectedTexturePath) { continue; }
488  {
489  var scaledRect = new Rectangle(textureRect.Location + sprite.SourceRect.Location.Multiply(zoom), sprite.SourceRect.Size.Multiply(zoom));
490  if (scaledRect.Contains(PlayerInput.MousePosition))
491  {
492  spriteList.Select(sprite, autoScroll: GUIListBox.AutoScroll.Disabled);
493  UpdateScrollBar(spriteList);
494  UpdateScrollBar(textureList);
495  // Release the keyboard so that we can nudge the source rects
496  GUI.KeyboardDispatcher.Subscriber = null;
497  }
498  }
499  }
500  }
501  }
502  if (GUI.MouseOn == null)
503  {
504  if (PlayerInput.ScrollWheelSpeed != 0)
505  {
506  float newZoom = MathHelper.Clamp(zoom + PlayerInput.ScrollWheelSpeed * (float)deltaTime * 0.05f * zoom, MinZoom, MaxZoom);
507  float zoomDeltaPrc = ((newZoom - zoom) / zoom);
508  zoom = newZoom;
509  zoomBar.BarScroll = GetBarScrollValue();
510 
511  // modify view area offset as well when zooming, to zoom into mouse cursor position
512  Point mouseToViewAreaScreenCenterDelta = (GetViewArea.Center - viewAreaOffset) - PlayerInput.MousePosition.ToPoint();
513  Vector2 mouseDelta = mouseToViewAreaScreenCenterDelta.ToVector2();
514 
515  Vector2 newViewAreaOffset = viewAreaOffset.ToVector2();
516  newViewAreaOffset += (mouseDelta + newViewAreaOffset) * zoomDeltaPrc;
517  viewAreaOffset = newViewAreaOffset.ToPoint();
518  }
519  widgets.Values.ForEach(w => w.Update((float)deltaTime));
521  {
522  // "Camera" Pan
523  Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 100.0f;
524  viewAreaOffset += moveSpeed.ToPoint();
525  }
526  }
527  if (GUI.KeyboardDispatcher.Subscriber == null)
528  {
529  if (PlayerInput.KeyDown(Keys.LeftControl) && PlayerInput.KeyHit(Keys.C))
530  {
531  string text = "";
532  if (selectedSprites.Count == 1)
533  {
534  var selectedSprite = selectedSprites.First();
535  if (selectedSprite.SourceElement != null)
536  {
537  string sourceRectText = $"sourcerect=\"{XMLExtensions.RectToString(selectedSprite.SourceRect)}\"";
538  text += $"{sourceRectText}";
539  }
540  }
541  else
542  {
543  foreach (var selectedSprite in selectedSprites)
544  {
545  if (selectedSprite.SourceElement == null) { continue; }
546  XElement xElement = new XElement(selectedSprite.SourceElement.Element);
547  xElement.SetAttributeValue("sourcerect", XMLExtensions.RectToString(selectedSprite.SourceRect));
548  xElement.SetAttributeValue("origin", XMLExtensions.Vector2ToString(selectedSprite.RelativeOrigin));
549  text += $"{xElement}";
550  if (selectedSprites.Last() != selectedSprite)
551  {
552  text += Environment.NewLine;
553  }
554  }
555  }
556 
557  Clipboard.SetText(text);
558  }
559  if (PlayerInput.KeyHit(Keys.Left))
560  {
561  Nudge(Keys.Left);
562  }
563  if (PlayerInput.KeyHit(Keys.Right))
564  {
565  Nudge(Keys.Right);
566  }
567  if (PlayerInput.KeyHit(Keys.Down))
568  {
569  Nudge(Keys.Down);
570  }
571  if (PlayerInput.KeyHit(Keys.Up))
572  {
573  Nudge(Keys.Up);
574  }
575  if (PlayerInput.KeyDown(Keys.Left))
576  {
577  holdTimer += deltaTime;
578  if (holdTimer > holdTime)
579  {
580  Nudge(Keys.Left);
581  }
582  }
583  else if (PlayerInput.KeyDown(Keys.Right))
584  {
585  holdTimer += deltaTime;
586  if (holdTimer > holdTime)
587  {
588  Nudge(Keys.Right);
589  }
590  }
591  else if (PlayerInput.KeyDown(Keys.Down))
592  {
593  holdTimer += deltaTime;
594  if (holdTimer > holdTime)
595  {
596  Nudge(Keys.Down);
597  }
598  }
599  else if (PlayerInput.KeyDown(Keys.Up))
600  {
601  holdTimer += deltaTime;
602  if (holdTimer > holdTime)
603  {
604  Nudge(Keys.Up);
605  }
606  }
607  else
608  {
609  holdTimer = 0;
610  }
611 
612  float moveSpeed = 600f * zoom;
613  float moveSpeedDeltaTime = (float)(moveSpeed * deltaTime);
614  Vector2 viewOffsetMove = Vector2.Zero;
615  if (PlayerInput.KeyDown(Keys.W))
616  {
617  viewOffsetMove.Y += moveSpeedDeltaTime;
618  }
619  if (PlayerInput.KeyDown(Keys.S))
620  {
621  viewOffsetMove.Y -= moveSpeedDeltaTime;
622  }
623  if (PlayerInput.KeyDown(Keys.A))
624  {
625  viewOffsetMove.X += moveSpeedDeltaTime;
626  }
627  if (PlayerInput.KeyDown(Keys.D))
628  {
629  viewOffsetMove.X -= moveSpeedDeltaTime;
630  }
631 
632  viewAreaOffset += viewOffsetMove.ToPoint();
633  }
634  }
635 
636  private double holdTimer;
637  private readonly float holdTime = 0.2f;
638  private void Nudge(Keys key)
639  {
640  switch (key)
641  {
642  case Keys.Left:
643  foreach (var sprite in selectedSprites)
644  {
645  var newRect = sprite.SourceRect;
646  if (ControlDown)
647  {
648  newRect.Width--;
649  }
650  else
651  {
652  newRect.X--;
653  }
654  UpdateSourceRect(sprite, newRect);
655  }
656  break;
657  case Keys.Right:
658  foreach (var sprite in selectedSprites)
659  {
660  var newRect = sprite.SourceRect;
661  if (ControlDown)
662  {
663  newRect.Width++;
664  }
665  else
666  {
667  newRect.X++;
668  }
669  UpdateSourceRect(sprite, newRect);
670  }
671  break;
672  case Keys.Down:
673  foreach (var sprite in selectedSprites)
674  {
675  var newRect = sprite.SourceRect;
676  if (ControlDown)
677  {
678  newRect.Height++;
679  }
680  else
681  {
682  newRect.Y++;
683  }
684  UpdateSourceRect(sprite, newRect);
685  }
686  break;
687  case Keys.Up:
688  foreach (var sprite in selectedSprites)
689  {
690  var newRect = sprite.SourceRect;
691  if (ControlDown)
692  {
693  newRect.Height--;
694  }
695  else
696  {
697  newRect.Y--;
698  }
699  UpdateSourceRect(sprite, newRect);
700  }
701  break;
702  }
703  }
704 
705 
706  public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
707  {
708  graphics.Clear(backgroundColor);
709  spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable, samplerState: SamplerState.PointClamp);
710 
711  var viewArea = GetViewArea;
712 
713  if (SelectedTexture != null)
714  {
715  textureRect = new Rectangle(
716  (int)(viewArea.Center.X - SelectedTexture.Bounds.Width / 2f * zoom),
717  (int)(viewArea.Center.Y - SelectedTexture.Bounds.Height / 2f * zoom),
718  (int)(SelectedTexture.Bounds.Width * zoom),
719  (int)(SelectedTexture.Bounds.Height * zoom));
720 
721  spriteBatch.Draw(SelectedTexture,
722  viewArea.Center.ToVector2(),
723  sourceRectangle: null,
724  color: Color.White,
725  rotation: 0.0f,
726  origin: new Vector2(SelectedTexture.Bounds.Width / 2.0f, SelectedTexture.Bounds.Height / 2.0f),
727  scale: zoom,
728  effects: SpriteEffects.None,
729  layerDepth: 0);
730 
731  //GUI.DrawRectangle(spriteBatch, viewArea, Color.Green, isFilled: false);
732  GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled: false);
733 
734  if (drawGrid)
735  {
736  DrawGrid(spriteBatch, textureRect, zoom, Submarine.GridSize);
737  }
738 
739  foreach (GUIComponent element in spriteList.Content.Children)
740  {
741  if (!(element.UserData is Sprite sprite)) { continue; }
742  if (sprite.FullPath != selectedTexturePath) { continue; }
743 
744  Rectangle sourceRect = new Rectangle(
745  textureRect.X + (int)(sprite.SourceRect.X * zoom),
746  textureRect.Y + (int)(sprite.SourceRect.Y * zoom),
747  (int)(sprite.SourceRect.Width * zoom),
748  (int)(sprite.SourceRect.Height * zoom));
749 
750  bool isSelected = selectedSprites.Contains(sprite);
751  GUI.DrawRectangle(spriteBatch, sourceRect, isSelected ? GUIStyle.Orange : GUIStyle.Red * 0.5f, thickness: isSelected ? 2 : 1);
752 
753  Identifier id = sprite.Identifier;
754  if (!id.IsEmpty)
755  {
756  int widgetSize = 10;
757  Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
758  Vector2 GetTopRight() => new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
759  Vector2 GetBottomRight() => new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
760  var originWidget = GetWidget($"{id}_origin", sprite, widgetSize, WidgetShape.Cross, initMethod: w =>
761  {
762  w.Tooltip = TextManager.AddPunctuation(':', originLabel, sprite.RelativeOrigin.FormatDoubleDecimal());
763  w.MouseHeld += dTime =>
764  {
765  w.DrawPos = PlayerInput.MousePosition.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
766  sprite.Origin = (w.DrawPos - textureRect.Location.ToVector2() - sprite.SourceRect.Location.ToVector2() * zoom) / zoom;
767  w.Tooltip = TextManager.AddPunctuation(':', originLabel, sprite.RelativeOrigin.FormatDoubleDecimal());
768  };
769  w.Refresh = () =>
770  w.DrawPos = (textureRect.Location.ToVector2() + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * zoom)
771  .Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
772  });
773  var positionWidget = GetWidget($"{id}_position", sprite, widgetSize, WidgetShape.Rectangle, initMethod: w =>
774  {
775  w.Tooltip = positionLabel + sprite.SourceRect.Location;
776  w.MouseHeld += dTime =>
777  {
778  w.DrawPos = (drawGrid && snapToGrid) ?
779  SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
780  PlayerInput.MousePosition;
781  w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
782  sprite.SourceRect = new Rectangle(((w.DrawPos - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size);
783  if (spriteList.SelectedComponent is GUITextBlock textBox)
784  {
785  // TODO: cache the sprite name?
786  textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
787  }
788  w.Tooltip = positionLabel + sprite.SourceRect.Location;
789  };
790  w.Refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom;
791  });
792  var sizeWidget = GetWidget($"{id}_size", sprite, widgetSize, WidgetShape.Rectangle, initMethod: w =>
793  {
794  w.Tooltip = TextManager.AddPunctuation(':', sizeLabel, sprite.SourceRect.Size.ToString());
795  w.MouseHeld += dTime =>
796  {
797  w.DrawPos = (drawGrid && snapToGrid) ?
798  SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
799  PlayerInput.MousePosition;
800  w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
801  sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - positionWidget.DrawPos) / zoom).ToPoint());
802  // TODO: allow to lock the origin
803  sprite.RelativeOrigin = sprite.RelativeOrigin;
804  if (spriteList.SelectedComponent is GUITextBlock textBox)
805  {
806  // TODO: cache the sprite name?
807  textBox.Text = GetSpriteName(sprite) + " " + sprite.SourceRect;
808  }
809  w.Tooltip = TextManager.AddPunctuation(':', sizeLabel, sprite.SourceRect.Size.ToString());
810  };
811  w.Refresh = () => w.DrawPos = textureRect.Location.ToVector2() + new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom;
812  });
813  originWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber = null;
814  positionWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber = null;
815  sizeWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber = null;
816  if (isSelected)
817  {
818  positionWidget.Draw(spriteBatch, (float)deltaTime);
819  sizeWidget.Draw(spriteBatch, (float)deltaTime);
820  originWidget.Draw(spriteBatch, (float)deltaTime);
821  }
822  }
823  }
824  }
825 
826  GUI.Draw(Cam, spriteBatch);
827 
828  spriteBatch.End();
829  }
830 
831  private void DrawGrid(SpriteBatch spriteBatch, Rectangle gridArea, float zoom, Vector2 gridSize)
832  {
833  gridSize *= zoom;
834  if (gridSize.X < 1.0f) { return; }
835  if (gridSize.Y < 1.0f) { return; }
836  int xLines = (int)(gridArea.Width / gridSize.X);
837  int yLines = (int)(gridArea.Height / gridSize.Y);
838 
839  for (int x = 0; x <= xLines; x++)
840  {
841  GUI.DrawLine(spriteBatch,
842  new Vector2(gridArea.X + x * gridSize.X, gridArea.Y),
843  new Vector2(gridArea.X + x * gridSize.X, gridArea.Bottom),
844  Color.White * 0.25f);
845  }
846  for (int y = 0; y <= yLines; y++)
847  {
848  GUI.DrawLine(spriteBatch,
849  new Vector2(gridArea.X, gridArea.Y + y * gridSize.Y),
850  new Vector2(gridArea.Right, gridArea.Y + y * gridSize.Y),
851  Color.White * 0.25f);
852  }
853  }
854 
855  private Vector2 SnapToGrid(Vector2 position, Rectangle gridArea, float zoom, Vector2 gridSize, float tolerance)
856  {
857  gridSize *= zoom;
858  if (gridSize.X < 1.0f) { return position; }
859  if (gridSize.Y < 1.0f) { return position; }
860 
861  Vector2 snappedPos = position;
862  snappedPos.X -= gridArea.X;
863  snappedPos.Y -= gridArea.Y;
864 
865  Vector2 gridPos = new Vector2(
866  MathUtils.RoundTowardsClosest(snappedPos.X, gridSize.X),
867  MathUtils.RoundTowardsClosest(snappedPos.Y, gridSize.Y));
868 
869  if (Math.Abs(gridPos.X - snappedPos.X) < tolerance)
870  {
871  snappedPos.X = gridPos.X;
872  }
873  if (Math.Abs(gridPos.Y - snappedPos.Y) < tolerance)
874  {
875  snappedPos.Y = gridPos.Y;
876  }
877 
878  snappedPos.X += gridArea.X;
879  snappedPos.Y += gridArea.Y;
880  return snappedPos;
881  }
882 
883  private void FilterTextures(string text)
884  {
885  if (string.IsNullOrWhiteSpace(text))
886  {
887  filterTexturesLabel.Visible = true;
888  textureList.Content.Children.ForEach(c => c.Visible = true);
889  return;
890  }
891  text = text.ToLower();
892  filterTexturesLabel.Visible = false;
893  foreach (GUIComponent child in textureList.Content.Children)
894  {
895  if (!(child is GUITextBlock textBlock)) { continue; }
896  textBlock.Visible = textBlock.Text.Contains(text, StringComparison.OrdinalIgnoreCase);
897  }
898  }
899  private void FilterSprites(string text)
900  {
901  if (string.IsNullOrWhiteSpace(text))
902  {
903  filterSpritesLabel.Visible = true;
904  spriteList.Content.Children.ForEach(c => c.Visible = true);
905  return;
906  }
907  text = text.ToLower();
908  filterSpritesLabel.Visible = false;
909  foreach (GUIComponent child in spriteList.Content.Children)
910  {
911  if (!(child is GUITextBlock textBlock)) { continue; }
912  textBlock.Visible = textBlock.Text.Contains(text, StringComparison.OrdinalIgnoreCase);
913  }
914  }
915 
916  public override void Select()
917  {
918  base.Select();
919  LoadSprites();
920  RefreshLists();
921  spriteList.Select(0, autoScroll: GUIListBox.AutoScroll.Disabled);
922  }
923 
924  protected override void DeselectEditorSpecific()
925  {
926  loadedSprites.ForEach(s => s.Remove());
927  loadedSprites.Clear();
928  ResetWidgets();
929  // Automatically reload all sprites that have been selected at least once (and thus might have been edited)
930  var reloadedSprites = new List<Sprite>();
931  foreach (var sprite in dirtySprites)
932  {
933  foreach (var s in Sprite.LoadedSprites)
934  {
935  if (s.FullPath == sprite.FullPath && !reloadedSprites.Contains(s))
936  {
937  s.ReloadXML();
938  reloadedSprites.Add(s);
939  }
940  }
941  }
942  dirtySprites.Clear();
943  filterSpritesBox.Text = "";
944  filterTexturesBox.Text = "";
945  }
946 
947  public void SelectSprite(Sprite sprite)
948  {
949  lastSprite = sprite;
950  if (!loadedSprites.Contains(sprite))
951  {
952  loadedSprites.Add(sprite);
953  RefreshLists();
954  }
955  if (selectedSprites.Any(s => s.FullPath != selectedTexturePath))
956  {
957  ResetWidgets();
958  }
959  if (Widget.EnableMultiSelect)
960  {
961  if (selectedSprites.Contains(sprite))
962  {
963  selectedSprites.Remove(sprite);
964  }
965  else
966  {
967  selectedSprites.Add(sprite);
968  dirtySprites.Add(sprite);
969  }
970  }
971  else
972  {
973  selectedSprites.Clear();
974  selectedSprites.Add(sprite);
975  dirtySprites.Add(sprite);
976  }
977  if (sprite.FullPath != selectedTexturePath)
978  {
979  textureList.Select(sprite.FullPath, autoScroll: GUIListBox.AutoScroll.Disabled);
980  UpdateScrollBar(textureList);
981  }
982  xmlPathText.Text = string.Empty;
983  foreach (var s in selectedSprites)
984  {
985  texturePathText.Text = s.FilePath.Value;
986  var element = s.SourceElement;
987  if (element != null)
988  {
989  string xmlPath = element.ParseContentPathFromUri();
990  if (!xmlPathText.Text.Contains(xmlPath))
991  {
992  xmlPathText.Text += "\n" + xmlPath;
993  }
994  }
995  }
996  xmlPathText.TextColor = Color.LightGray;
997  }
998 
999  public void RefreshLists()
1000  {
1001  selectedSprites.Clear();
1002  textureList.ClearChildren();
1003  spriteList.ClearChildren();
1004  ResetWidgets();
1005  HashSet<string> textures = new HashSet<string>();
1006  // Create texture list
1007  foreach (Sprite sprite in loadedSprites.OrderBy(s => Path.GetFileNameWithoutExtension(s.FilePath.Value)))
1008  {
1009  //ignore sprites that don't have a file path (e.g. submarine pics)
1010  if (sprite.FilePath.IsNullOrEmpty()) { continue; }
1011  string normalizedFilePath = sprite.FilePath.FullPath;
1012  if (!textures.Contains(normalizedFilePath))
1013  {
1014  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), textureList.Content.RectTransform) { MinSize = new Point(0, 20) },
1015  Path.GetFileName(sprite.FilePath.Value))
1016  {
1017  ToolTip = sprite.FilePath.Value,
1018  UserData = sprite.FullPath
1019  };
1020  textures.Add(normalizedFilePath);
1021  }
1022  }
1023  // Create sprite list
1024  // TODO: allow the user to choose whether to sort by file name or by texture sheet
1025  //foreach (Sprite sprite in loadedSprites.OrderBy(s => GetSpriteName(s)))
1026  foreach (Sprite sprite in loadedSprites.OrderBy(s => s.SourceElement.GetAttributeContentPath("texture")?.Value ?? string.Empty))
1027  {
1028  string elementLocalName = sprite.SourceElement.Element.Name.LocalName;
1029  string text = $"{GetSpriteName(sprite)} ({sprite.SourceRect.X}, {sprite.SourceRect.Y}, {sprite.SourceRect.Width}, {sprite.SourceRect.Height}) [{elementLocalName}]";
1030  if (string.Equals(elementLocalName, "sprite", StringComparison.InvariantCultureIgnoreCase))
1031  {
1032  text = $"{GetSpriteName(sprite)} ({sprite.SourceRect.X}, {sprite.SourceRect.Y}, {sprite.SourceRect.Width}, {sprite.SourceRect.Height})";
1033  }
1034 
1035  new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), spriteList.Content.RectTransform) { MinSize = new Point(0, 20) }, text: text)
1036  {
1037  UserData = sprite
1038  };
1039  }
1040  topPanelContents.Visible = false;
1041  }
1042 
1043  public void ResetZoom()
1044  {
1045  if (SelectedTexture == null) { return; }
1046  var viewArea = GetViewArea;
1047  float width = viewArea.Width / (float)SelectedTexture.Width;
1048  float height = viewArea.Height / (float)SelectedTexture.Height;
1049  zoom = Math.Min(1, Math.Min(width, height));
1050  zoomBar.BarScroll = GetBarScrollValue();
1051  viewAreaOffset = Point.Zero;
1052  }
1053 #endregion
1054 
1055  #region Helpers
1056  private Point viewAreaOffset;
1057  private Rectangle GetViewArea
1058  {
1059  get
1060  {
1061  int margin = 20;
1062  var viewArea = new Rectangle(leftPanel.Rect.Right + margin + viewAreaOffset.X, topPanel.Rect.Bottom + margin + viewAreaOffset.Y, rightPanel.Rect.Left - leftPanel.Rect.Right - margin * 2, Frame.Rect.Height - topPanel.Rect.Height - margin * 2);
1063  return viewArea;
1064  }
1065  }
1066 
1067  private float GetBarScrollValue() => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(MinZoom, MaxZoom, zoom));
1068 
1069  private string GetSpriteName(Sprite sprite)
1070  {
1071  var sourceElement = sprite.SourceElement;
1072  if (sourceElement == null) { return string.Empty; }
1073  string name = sprite.Name;
1074  if (string.IsNullOrWhiteSpace(name))
1075  {
1076  name = sourceElement.Parent?.GetAttributeString("identifier", string.Empty);
1077  }
1078  if (string.IsNullOrEmpty(name))
1079  {
1080  name = sourceElement.Parent?.GetAttributeString("name", string.Empty);
1081  }
1082  return string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(sprite.FilePath.Value) : name;
1083  }
1084 
1085  private void UpdateScrollBar(GUIListBox listBox)
1086  {
1087  var sb = listBox.ScrollBar;
1088  sb.BarScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, listBox.Content.CountChildren - 1, listBox.SelectedIndex)), sb.MinValue, sb.MaxValue);
1089  }
1090 
1091  private void UpdateSourceRect(Sprite sprite, Rectangle newRect)
1092  {
1093  sprite.SourceRect = newRect;
1094  // Keeps the relative origin unchanged. The absolute origin will be recalculated.
1095  sprite.RelativeOrigin = sprite.RelativeOrigin;
1096  }
1097 #endregion
1098 
1099  #region Widgets
1100  private Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
1101 
1102  private Widget GetWidget(string id, Sprite sprite, int size = 5, WidgetShape shape = WidgetShape.Rectangle, Action<Widget> initMethod = null)
1103  {
1104  if (!widgets.TryGetValue(id, out Widget widget))
1105  {
1106  int selectedSize = (int)Math.Round(size * 1.5f);
1107  widget = new Widget(id, size, shape)
1108  {
1109  Data = sprite,
1110  Color = Color.Yellow,
1111  SecondaryColor = Color.Gray,
1112  TooltipOffset = new Vector2(selectedSize / 2 + 5, -10)
1113  };
1114  widget.PreDraw += (sp, dTime) =>
1115  {
1116  if (!widget.IsControlled)
1117  {
1118  widget.Refresh();
1119  }
1120  };
1121  widget.PreUpdate += dTime => widget.Enabled = selectedSprites.Contains(sprite);
1122  widget.PostUpdate += dTime =>
1123  {
1124  widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
1125  widget.Size = widget.IsSelected ? selectedSize : size;
1126  widget.IsFilled = widget.IsControlled;
1127  };
1128  widgets.Add(id, widget);
1129  initMethod?.Invoke(widget);
1130  }
1131  return widget;
1132  }
1133 
1134  private void ResetWidgets()
1135  {
1136  widgets.Clear();
1137  Widget.SelectedWidgets.Clear();
1138  }
1139 #endregion
1140  }
1141 }
string???????????? Value
Definition: ContentPath.cs:27
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
virtual Rectangle Rect
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:42
void Select(object userData, Force force=Force.No, AutoScroll autoScroll=AutoScroll.Enabled)
Definition: GUIListBox.cs:449
override void ClearChildren()
Definition: GUIListBox.cs:1264
OnTextChangedHandler OnTextChanged
Don't set the Text property on delegates that register to this event, because modifying the Text will...
Definition: GUITextBox.cs:38
static RasterizerState ScissorTestEnable
Definition: GameMain.cs:195
Action ResolutionChanged
NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer...
Definition: GameMain.cs:133
static GameMain Instance
Definition: GameMain.cs:144
static bool KeyDown(InputType inputType)
Vector2 RelativeSize
Relative to the parent rect.
Point?? MinSize
Min size in pixels. Does not affect scaling.
bool Contains(string str, StringComparison stringComparison=StringComparison.Ordinal)
override void Update(double deltaTime)
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
override void AddToGUIUpdateList()
By default, submits the screen's main GUIFrame and, if requested upon construction,...
static IEnumerable< Sprite > LoadedSprites
NumberType
Definition: Enums.cs:741
WidgetShape
Definition: Widget.cs:11