1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using Microsoft.Xna.Framework.Input;
5 using System.Collections.Generic;
25 private GUIFrame backgroundColorPanel;
27 private bool drawGrid, snapToGrid;
33 private readonly List<Sprite> selectedSprites =
new List<Sprite>();
34 private readonly List<Sprite> dirtySprites =
new List<Sprite>();
35 private Texture2D SelectedTexture => lastSprite?.Texture;
37 private string selectedTexturePath;
40 private float zoom = 1;
41 private const float MinZoom = 0.25f, MaxZoom = 10.0f;
50 private bool editBackgroundColor;
51 private Color backgroundColor =
new Color(0.051f, 0.149f, 0.271f, 1.0f);
55 private readonly
Camera cam;
63 get {
return topPanel; }
73 #region Initialization
74 private void CreateUI()
76 originLabel = TextManager.Get(
"charactereditor.origin");
77 positionLabel = TextManager.GetWithVariable(
"charactereditor.position",
"[coordinates]",
string.Empty);
78 sizeLabel = TextManager.Get(
"charactereditor.size");
85 RelativeOffset = new Vector2(0, 0.1f)
86 }, TextManager.Get(
"spriteeditor.reloadtexture"))
88 OnClicked = (button, userData) =>
90 var selected = selectedSprites.ToList();
91 Sprite firstSelected = selected.First();
92 selected.ForEach(s => s.ReloadTexture());
96 texturePathText.
Text = TextManager.GetWithVariable(
"spriteeditor.texturesreloaded",
"[filepath]", firstSelected.
FilePath.
Value);
97 texturePathText.
TextColor = GUIStyle.Green;
101 new GUIButton(
new RectTransform(
new Vector2(0.14f, 0.4f), topPanelContents.
RectTransform,
Anchor.BottomLeft)
103 RelativeOffset = new Vector2(0, 0.1f)
104 }, TextManager.Get(
"spriteeditor.resetchanges"))
106 OnClicked = (button, userData) =>
108 if (SelectedTexture ==
null) {
return false; }
109 foreach (Sprite sprite
in loadedSprites)
111 if (sprite.FullPath != selectedTexturePath) {
continue; }
112 var element = sprite.SourceElement;
113 if (element ==
null) {
continue; }
115 sprite.SourceRect = element.GetAttributeRect(
"sourcerect", sprite.SourceRect);
116 sprite.RelativeOrigin = element.GetAttributeVector2(
"origin",
new Vector2(0.5f, 0.5f));
119 xmlPathText.
Text = TextManager.Get(
"spriteeditor.resetsuccessful");
124 new GUIButton(
new RectTransform(
new Vector2(0.14f, 0.4f), topPanelContents.
RectTransform,
Anchor.TopLeft)
126 RelativeOffset = new Vector2(0.15f, 0.1f)
127 }, TextManager.Get(
"spriteeditor.saveselectedsprites"))
129 OnClicked = (button, userData) =>
131 return SaveSprites(selectedSprites);
134 new GUIButton(
new RectTransform(
new Vector2(0.14f, 0.4f), topPanelContents.
RectTransform,
Anchor.BottomLeft)
136 RelativeOffset = new Vector2(0.15f, 0.1f)
137 }, TextManager.Get(
"spriteeditor.saveallsprites"))
139 OnClicked = (button, userData) =>
141 return SaveSprites(loadedSprites);
145 GUITextBlock.AutoScaleAndNormalize(topPanelContents.
Children.Where(c => c is GUIButton).Select(c => ((GUIButton)c).TextBlock));
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)
150 RelativeOffset = new Vector2(0.05f, 0.3f)
151 }, style:
"GUISlider", barSize: 0.1f)
153 BarScroll = GetBarScrollValue(),
155 OnMoved = (scrollBar, value) =>
157 zoom = MathHelper.Lerp(MinZoom, MaxZoom, value);
158 viewAreaOffset = Point.Zero;
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"))
164 OnClicked = (box, data) =>
170 resetBtn.TextBlock.AutoScaleHorizontal =
true;
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"))
175 OnSelected = (tickBox) =>
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"))
184 OnSelected = (tickBox) =>
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);
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 };
199 var filterArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.03f), paddedLeftPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal:
true)
202 UserData =
"filterarea"
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);
207 filterTexturesBox.
OnTextChanged += (textBox, text) => { FilterTextures(text);
return true; };
209 textureList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 1.0f), paddedLeftPanel.RectTransform))
211 PlaySoundOnSelect =
true,
212 OnSelected = (listBox, userData) =>
214 var newTexturePath = userData as string;
215 if (selectedTexturePath ==
null || selectedTexturePath != newTexturePath)
217 selectedTexturePath = newTexturePath;
219 spriteList.
Select(loadedSprites.First(s => s.FilePath == selectedTexturePath), autoScroll: GUIListBox.AutoScroll.Disabled);
220 UpdateScrollBar(spriteList);
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; }
229 texturePathText.
TextColor = Color.LightGray;
230 topPanelContents.
Visible =
true;
236 var paddedRightPanel =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.95f, 0.95f), rightPanel.
RectTransform,
Anchor.Center))
239 RelativeSpacing = 0.01f
242 filterArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform) { MinSize = new Point(0, 20) }, isHorizontal:
true)
245 UserData =
"filterarea"
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);
250 filterSpritesBox.
OnTextChanged += (textBox, text) => { FilterSprites(text);
return true; };
252 spriteList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 1.0f), paddedRightPanel.RectTransform))
254 PlaySoundOnSelect =
true,
255 OnSelected = (listBox, userData) =>
257 if (userData is Sprite sprite)
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"))
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)
281 AbsoluteOffset = new Point(20, 0)
282 }, isHorizontal:
true, childAnchor:
Anchor.CenterRight)
285 RelativeSpacing = 0.01f
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--)
291 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.2f, 1), inputArea.RectTransform)
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)
300 Font = GUIStyle.SmallFont
302 numberInput.MinValueInt = 0;
303 numberInput.MaxValueInt = 255;
304 numberInput.Font = GUIStyle.SmallFont;
308 colorLabel.TextColor = GUIStyle.Red;
309 numberInput.IntValue = backgroundColor.R;
310 numberInput.OnValueChanged += (numInput) => backgroundColor.R = (
byte)(numInput.IntValue);
313 colorLabel.TextColor = GUIStyle.Green;
314 numberInput.IntValue = backgroundColor.G;
315 numberInput.OnValueChanged += (numInput) => backgroundColor.G = (
byte)(numInput.IntValue);
318 colorLabel.TextColor = Color.DeepSkyBlue;
319 numberInput.IntValue = backgroundColor.B;
320 numberInput.OnValueChanged += (numInput) => backgroundColor.B = (
byte)(numInput.IntValue);
326 private readonly HashSet<Sprite> loadedSprites =
new HashSet<Sprite>();
327 private void LoadSprites()
329 loadedSprites.ForEach(s => s.Remove());
330 loadedSprites.Clear();
331 var contentPackages = ContentPackageManager.EnabledPackages.All.ToList();
334 var vanilla = GameMain.VanillaContent;
337 contentPackages.Remove(vanilla);
340 foreach (var contentPackage
in contentPackages)
342 foreach (var file
in contentPackage.Files)
344 if (file.Path.EndsWith(
".xml"))
346 XDocument doc = XMLExtensions.TryLoadXml(file.Path);
349 LoadSprites(doc.Root.FromPackage(file.Path.ContentPackage));
355 void LoadSprites(ContentXElement element)
357 string[] spriteElementNames = {
375 foreach (
string spriteElementName
in spriteElementNames)
377 element.GetChildElements(spriteElementName).ForEach(s => CreateSprite(s));
380 element.Elements().ForEach(e => LoadSprites(e));
383 void CreateSprite(ContentXElement element)
386 if (element.Attributes().None()) {
return; }
388 string spriteFolder =
"";
389 ContentPath texturePath =
null;
391 if (element.GetAttribute(
"texture") !=
null)
393 texturePath = element.GetAttributeContentPath(
"texture");
397 if (element.Name.ToString().ToLower() ==
"vinesprite")
399 texturePath = element.Parent.GetAttributeContentPath(
"vineatlas");
402 if (texturePath.IsNullOrEmpty()) {
return; }
405 if (texturePath.Value.Contains(
"[GENDER]") || texturePath.Value.Contains(
"[HEADID]") || texturePath.Value.Contains(
"[RACE]") || texturePath.Value.Contains(
"[VARIANT]")) {
return; }
406 if (!texturePath.Value.Contains(
"/"))
408 var parsedPath = element.ParseContentPathFromUri();
409 spriteFolder = Path.GetDirectoryName(parsedPath);
417 loadedSprites.Add(
new Sprite(element, spriteFolder, texturePath.Value, lazyLoad:
true));
421 private bool SaveSprites(IEnumerable<Sprite> sprites)
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)
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));
443 docsToSave.Add(element.Document);
445 xmlPathText.
Text = TextManager.Get(
"spriteeditor.allchangessavedto");
446 foreach (XDocument doc
in docsToSave)
448 string xmlPath = doc.ParseContentPathFromUri();
449 xmlPathText.
Text +=
"\n" + xmlPath;
453 doc.SaveSafe(xmlPath);
461 #region Public methods
468 if (editBackgroundColor)
474 public override void Update(
double deltaTime)
476 base.Update(deltaTime);
477 Widget.EnableMultiSelect = ControlDown;
480 if (Widget.SelectedWidgets.None() || Widget.EnableMultiSelect)
482 if (SelectedTexture !=
null && GUI.MouseOn ==
null)
484 foreach (
Sprite sprite
in loadedSprites)
486 if (sprite.
FullPath != selectedTexturePath) {
continue; }
493 UpdateScrollBar(spriteList);
494 UpdateScrollBar(textureList);
496 GUI.KeyboardDispatcher.Subscriber =
null;
502 if (GUI.MouseOn ==
null)
509 widgets.Values.ForEach(w => w.Update((
float)deltaTime));
514 viewAreaOffset += moveSpeed.ToPoint();
517 if (GUI.KeyboardDispatcher.Subscriber ==
null)
537 holdTimer += deltaTime;
538 if (holdTimer > holdTime)
545 holdTimer += deltaTime;
546 if (holdTimer > holdTime)
553 holdTimer += deltaTime;
554 if (holdTimer > holdTime)
561 holdTimer += deltaTime;
562 if (holdTimer > holdTime)
574 private double holdTimer;
575 private readonly
float holdTime = 0.2f;
576 private void Nudge(Keys key)
581 foreach (var sprite
in selectedSprites)
583 var newRect = sprite.SourceRect;
592 UpdateSourceRect(sprite, newRect);
596 foreach (var sprite
in selectedSprites)
598 var newRect = sprite.SourceRect;
607 UpdateSourceRect(sprite, newRect);
611 foreach (var sprite
in selectedSprites)
613 var newRect = sprite.SourceRect;
622 UpdateSourceRect(sprite, newRect);
626 foreach (var sprite
in selectedSprites)
628 var newRect = sprite.SourceRect;
637 UpdateSourceRect(sprite, newRect);
644 public override void Draw(
double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
646 graphics.Clear(backgroundColor);
647 spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState:
GameMain.
ScissorTestEnable, samplerState: SamplerState.PointClamp);
649 var viewArea = GetViewArea;
651 if (SelectedTexture !=
null)
654 (
int)(viewArea.Center.X - SelectedTexture.Bounds.Width / 2f * zoom),
655 (
int)(viewArea.Center.Y - SelectedTexture.Bounds.Height / 2f * zoom),
656 (
int)(SelectedTexture.Bounds.Width * zoom),
657 (
int)(SelectedTexture.Bounds.Height * zoom));
659 spriteBatch.Draw(SelectedTexture,
660 viewArea.Center.ToVector2(),
661 sourceRectangle:
null,
664 origin:
new Vector2(SelectedTexture.Bounds.Width / 2.0f, SelectedTexture.Bounds.Height / 2.0f),
666 effects: SpriteEffects.None,
670 GUI.DrawRectangle(spriteBatch, textureRect, Color.Gray, isFilled:
false);
680 if (sprite.FullPath != selectedTexturePath) {
continue; }
683 textureRect.X + (
int)(sprite.SourceRect.X * zoom),
684 textureRect.Y + (
int)(sprite.SourceRect.Y * zoom),
685 (
int)(sprite.SourceRect.Width * zoom),
686 (
int)(sprite.SourceRect.Height * zoom));
688 bool isSelected = selectedSprites.Contains(sprite);
689 GUI.DrawRectangle(spriteBatch, sourceRect, isSelected ? GUIStyle.Orange : GUIStyle.Red * 0.5f, thickness: isSelected ? 2 : 1);
691 Identifier
id = sprite.Identifier;
695 Vector2 GetTopLeft() => sprite.SourceRect.Location.ToVector2();
696 Vector2 GetTopRight() =>
new Vector2(GetTopLeft().X + sprite.SourceRect.Width, GetTopLeft().Y);
697 Vector2 GetBottomRight() =>
new Vector2(GetTopRight().X, GetTopRight().Y + sprite.SourceRect.Height);
698 var originWidget = GetWidget($
"{id}_origin", sprite, widgetSize,
WidgetShape.Cross, initMethod: w =>
700 w.Tooltip = TextManager.AddPunctuation(
':', originLabel, sprite.RelativeOrigin.FormatDoubleDecimal());
701 w.MouseHeld += dTime =>
703 w.DrawPos = PlayerInput.MousePosition.Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
704 sprite.Origin = (w.DrawPos - textureRect.Location.ToVector2() - sprite.SourceRect.Location.ToVector2() * zoom) / zoom;
705 w.Tooltip = TextManager.AddPunctuation(
':', originLabel, sprite.RelativeOrigin.FormatDoubleDecimal());
708 w.DrawPos = (textureRect.Location.ToVector2() + (sprite.Origin + sprite.SourceRect.Location.ToVector2()) * zoom)
709 .Clamp(textureRect.Location.ToVector2() + GetTopLeft() * zoom, textureRect.Location.ToVector2() + GetBottomRight() * zoom);
711 var positionWidget = GetWidget($
"{id}_position", sprite, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
713 w.Tooltip = positionLabel + sprite.SourceRect.Location;
714 w.MouseHeld += dTime =>
716 w.DrawPos = (drawGrid && snapToGrid) ?
717 SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
718 PlayerInput.MousePosition;
719 w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
720 sprite.SourceRect = new Rectangle(((w.DrawPos - textureRect.Location.ToVector2()) / zoom).ToPoint(), sprite.SourceRect.Size);
721 if (spriteList.SelectedComponent is GUITextBlock textBox)
724 textBox.Text = GetSpriteName(sprite) +
" " + sprite.SourceRect;
726 w.Tooltip = positionLabel + sprite.SourceRect.Location;
728 w.Refresh = () => w.DrawPos = textureRect.Location.ToVector2() + sprite.SourceRect.Location.ToVector2() * zoom;
730 var sizeWidget = GetWidget($
"{id}_size", sprite, widgetSize,
WidgetShape.Rectangle, initMethod: w =>
732 w.Tooltip = TextManager.AddPunctuation(
':', sizeLabel, sprite.SourceRect.Size.ToString());
733 w.MouseHeld += dTime =>
735 w.DrawPos = (drawGrid && snapToGrid) ?
736 SnapToGrid(PlayerInput.MousePosition, textureRect, zoom, Submarine.GridSize, Submarine.GridSize.X / 4.0f * zoom) :
737 PlayerInput.MousePosition;
738 w.DrawPos = new Vector2((float)Math.Ceiling(w.DrawPos.X), (float)Math.Ceiling(w.DrawPos.Y));
739 sprite.SourceRect = new Rectangle(sprite.SourceRect.Location, ((w.DrawPos - positionWidget.DrawPos) / zoom).ToPoint());
741 sprite.RelativeOrigin = sprite.RelativeOrigin;
742 if (spriteList.SelectedComponent is GUITextBlock textBox)
745 textBox.Text = GetSpriteName(sprite) +
" " + sprite.SourceRect;
747 w.Tooltip = TextManager.AddPunctuation(
':', sizeLabel, sprite.SourceRect.Size.ToString());
749 w.Refresh = () => w.DrawPos = textureRect.Location.ToVector2() +
new Vector2(sprite.SourceRect.Right, sprite.SourceRect.Bottom) * zoom;
751 originWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber =
null;
752 positionWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber =
null;
753 sizeWidget.MouseDown += () => GUI.KeyboardDispatcher.Subscriber =
null;
756 positionWidget.Draw(spriteBatch, (
float)deltaTime);
757 sizeWidget.Draw(spriteBatch, (
float)deltaTime);
758 originWidget.Draw(spriteBatch, (
float)deltaTime);
764 GUI.Draw(Cam, spriteBatch);
769 private void DrawGrid(SpriteBatch spriteBatch, Rectangle gridArea,
float zoom, Vector2 gridSize)
772 if (gridSize.X < 1.0f) {
return; }
773 if (gridSize.Y < 1.0f) {
return; }
774 int xLines = (int)(gridArea.Width / gridSize.X);
775 int yLines = (int)(gridArea.Height / gridSize.Y);
777 for (
int x = 0; x <= xLines; x++)
779 GUI.DrawLine(spriteBatch,
780 new Vector2(gridArea.X + x * gridSize.X, gridArea.Y),
781 new Vector2(gridArea.X + x * gridSize.X, gridArea.Bottom),
782 Color.White * 0.25f);
784 for (
int y = 0; y <= yLines; y++)
786 GUI.DrawLine(spriteBatch,
787 new Vector2(gridArea.X, gridArea.Y + y * gridSize.Y),
788 new Vector2(gridArea.Right, gridArea.Y + y * gridSize.Y),
789 Color.White * 0.25f);
793 private Vector2 SnapToGrid(Vector2 position, Rectangle gridArea,
float zoom, Vector2 gridSize,
float tolerance)
796 if (gridSize.X < 1.0f) {
return position; }
797 if (gridSize.Y < 1.0f) {
return position; }
799 Vector2 snappedPos = position;
800 snappedPos.X -= gridArea.X;
801 snappedPos.Y -= gridArea.Y;
803 Vector2 gridPos =
new Vector2(
804 MathUtils.RoundTowardsClosest(snappedPos.X, gridSize.X),
805 MathUtils.RoundTowardsClosest(snappedPos.Y, gridSize.Y));
807 if (Math.Abs(gridPos.X - snappedPos.X) < tolerance)
809 snappedPos.X = gridPos.X;
811 if (Math.Abs(gridPos.Y - snappedPos.Y) < tolerance)
813 snappedPos.Y = gridPos.Y;
816 snappedPos.X += gridArea.X;
817 snappedPos.Y += gridArea.Y;
821 private void FilterTextures(
string text)
823 if (
string.IsNullOrWhiteSpace(text))
825 filterTexturesLabel.
Visible =
true;
829 text = text.ToLower();
830 filterTexturesLabel.
Visible =
false;
833 if (!(child is GUITextBlock textBlock)) {
continue; }
834 textBlock.Visible = textBlock.Text.Contains(text, StringComparison.OrdinalIgnoreCase);
837 private void FilterSprites(
string text)
839 if (
string.IsNullOrWhiteSpace(text))
841 filterSpritesLabel.
Visible =
true;
845 text = text.ToLower();
846 filterSpritesLabel.
Visible =
false;
849 if (!(child is GUITextBlock textBlock)) {
continue; }
850 textBlock.Visible = textBlock.Text.Contains(text, StringComparison.OrdinalIgnoreCase);
864 loadedSprites.ForEach(s => s.Remove());
865 loadedSprites.Clear();
868 var reloadedSprites =
new List<Sprite>();
869 foreach (var sprite
in dirtySprites)
873 if (s.FullPath == sprite.FullPath && !reloadedSprites.Contains(s))
876 reloadedSprites.Add(s);
880 dirtySprites.Clear();
881 filterSpritesBox.
Text =
"";
882 filterTexturesBox.
Text =
"";
888 if (!loadedSprites.Contains(sprite))
890 loadedSprites.Add(sprite);
893 if (selectedSprites.Any(s => s.FullPath != selectedTexturePath))
897 if (Widget.EnableMultiSelect)
899 if (selectedSprites.Contains(sprite))
901 selectedSprites.Remove(sprite);
905 selectedSprites.Add(sprite);
906 dirtySprites.Add(sprite);
911 selectedSprites.Clear();
912 selectedSprites.Add(sprite);
913 dirtySprites.Add(sprite);
915 if (sprite.
FullPath != selectedTexturePath)
918 UpdateScrollBar(textureList);
920 xmlPathText.
Text =
string.Empty;
921 foreach (var s
in selectedSprites)
923 texturePathText.
Text = s.FilePath.Value;
924 var element = s.SourceElement;
927 string xmlPath = element.ParseContentPathFromUri();
930 xmlPathText.
Text +=
"\n" + xmlPath;
939 selectedSprites.Clear();
943 HashSet<string> textures =
new HashSet<string>();
945 foreach (
Sprite sprite
in loadedSprites.OrderBy(s => Path.GetFileNameWithoutExtension(s.FilePath.Value)))
948 if (sprite.FilePath.IsNullOrEmpty()) {
continue; }
949 string normalizedFilePath = sprite.FilePath.FullPath;
950 if (!textures.Contains(normalizedFilePath))
953 Path.GetFileName(sprite.FilePath.Value))
955 ToolTip = sprite.FilePath.Value,
956 UserData = sprite.FullPath
958 textures.Add(normalizedFilePath);
964 foreach (
Sprite sprite
in loadedSprites.OrderBy(s => s.SourceElement.GetAttributeContentPath(
"texture")?.Value ??
string.Empty))
967 GetSpriteName(sprite) +
" (" + sprite.SourceRect.X +
", " + sprite.SourceRect.Y +
", " + sprite.SourceRect.Width +
", " + sprite.SourceRect.Height +
")")
972 topPanelContents.
Visible =
false;
977 if (SelectedTexture ==
null) {
return; }
978 var viewArea = GetViewArea;
979 float width = viewArea.Width / (float)SelectedTexture.Width;
980 float height = viewArea.Height / (
float)SelectedTexture.Height;
981 zoom = Math.Min(1, Math.Min(width, height));
983 viewAreaOffset = Point.Zero;
988 private Point viewAreaOffset;
994 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);
999 private float GetBarScrollValue() => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(MinZoom, MaxZoom, zoom));
1001 private string GetSpriteName(Sprite sprite)
1003 var sourceElement = sprite.SourceElement;
1004 if (sourceElement ==
null) {
return string.Empty; }
1005 string name = sprite.Name;
1006 if (
string.IsNullOrWhiteSpace(name))
1008 name = sourceElement.Parent.GetAttributeString(
"identifier",
string.Empty);
1010 if (
string.IsNullOrEmpty(name))
1012 name = sourceElement.Parent.GetAttributeString(
"name",
string.Empty);
1014 return string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(sprite.FilePath.Value) : name;
1017 private void UpdateScrollBar(GUIListBox listBox)
1019 var sb = listBox.ScrollBar;
1020 sb.BarScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, listBox.Content.CountChildren - 1, listBox.SelectedIndex)), sb.MinValue, sb.MaxValue);
1023 private void UpdateSourceRect(Sprite sprite, Rectangle newRect)
1025 sprite.SourceRect = newRect;
1027 sprite.RelativeOrigin = sprite.RelativeOrigin;
1032 private Dictionary<string, Widget> widgets =
new Dictionary<string, Widget>();
1034 private Widget GetWidget(
string id, Sprite sprite,
int size = 5, WidgetShape shape =
WidgetShape.Rectangle, Action<Widget> initMethod =
null)
1036 if (!widgets.TryGetValue(
id, out Widget widget))
1038 int selectedSize = (int)Math.Round(size * 1.5f);
1039 widget =
new Widget(
id, size, shape)
1042 Color = Color.Yellow,
1043 SecondaryColor = Color.Gray,
1044 TooltipOffset =
new Vector2(selectedSize / 2 + 5, -10)
1046 widget.PreDraw += (sp, dTime) =>
1048 if (!widget.IsControlled)
1053 widget.PreUpdate += dTime => widget.Enabled = selectedSprites.Contains(sprite);
1054 widget.PostUpdate += dTime =>
1056 widget.InputAreaMargin = widget.IsControlled ? 1000 : 0;
1057 widget.Size = widget.IsSelected ? selectedSize : size;
1058 widget.IsFilled = widget.IsControlled;
1060 widgets.Add(
id, widget);
1061 initMethod?.Invoke(widget);
1066 private void ResetWidgets()
1069 Widget.SelectedWidgets.Clear();
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
RectTransform RectTransform
IEnumerable< GUIComponent > Children
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
void Select(object userData, Force force=Force.No, AutoScroll autoScroll=AutoScroll.Enabled)
override void ClearChildren()
OnTextChangedHandler OnTextChanged
Don't set the Text property on delegates that register to this event, because modifying the Text will...
static RasterizerState ScissorTestEnable
Action ResolutionChanged
NOTE: Use very carefully. You need to ensure that you ALWAYS unsubscribe from this when you no longer...
bool Contains(string str, StringComparison stringComparison=StringComparison.Ordinal)
override void Update(double deltaTime)
override void DeselectEditorSpecific()
override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
void SelectSprite(Sprite sprite)
override void AddToGUIUpdateList()
By default, submits the screen's main GUIFrame and, if requested upon construction,...
static IEnumerable< Sprite > LoadedSprites
static readonly Vector2 GridSize