2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
9 using System.Diagnostics;
10 using System.Diagnostics.CodeAnalysis;
16 private readonly
int elementHeight;
18 private readonly
float inputFieldWidth = 0.5f;
19 private readonly
float largeInputFieldWidth = 0.8f;
21 public static List<string> MissingLocalizations =
new List<string>();
27 public static Tuple<SerializableProperty, PropertyCommand>
CommandBuffer;
29 private bool isReadonly;
35 foreach (var component
in Fields.SelectMany(f => f.Value))
46 component.Enabled = !value;
54 private Action refresh;
60 if (layoutGroup.NeedsToRecalculate) layoutGroup.Recalculate();
62 int spacing = layoutGroup.CountChildren == 0 ? 0 : ((layoutGroup.CountChildren - 1) * layoutGroup.AbsoluteSpacing);
63 return spacing + layoutGroup.Children.Sum(c => c.RectTransform.NonScaledSize.Y);
69 get {
return layoutGroup.CountChildren; }
75 public Dictionary<Identifier, GUIComponent[]>
Fields {
get;
private set; } =
new Dictionary<Identifier, GUIComponent[]>();
81 DebugConsole.ThrowError($
"No field for {property.Name} found!");
84 if (newValue is
float f)
86 foreach (var field
in fields)
92 numInput.FloatValue = f;
95 numInput.Flash(GUIStyle.Green);
101 else if (newValue is
int integer)
103 foreach (var field
in fields)
109 numInput.IntValue = integer;
112 numInput.Flash(GUIStyle.Green);
118 else if (newValue is
bool b)
122 tickBox.Selected = b;
125 tickBox.Flash(GUIStyle.Green);
129 else if (newValue is
string s)
136 textBox.Flash(GUIStyle.Green);
140 else if (newValue.GetType().IsEnum)
144 dropDown.Select((
int)newValue);
147 dropDown.Flash(GUIStyle.Green);
151 else if (newValue is Vector2 v2)
153 for (
int i = 0; i < fields.Length; i++)
155 var field = fields[i];
160 numInput.FloatValue = i == 0 ? v2.X : v2.Y;
163 numInput.Flash(GUIStyle.Green);
169 else if (newValue is Vector3 v3)
171 for (
int i = 0; i < fields.Length; i++)
173 var field = fields[i];
181 numInput.FloatValue = v3.X;
184 numInput.FloatValue = v3.Y;
187 numInput.FloatValue = v3.Z;
192 numInput.Flash(GUIStyle.Green);
198 else if (newValue is Vector4 v4)
200 for (
int i = 0; i < fields.Length; i++)
202 var field = fields[i];
210 numInput.FloatValue = v4.X;
213 numInput.FloatValue = v4.Y;
216 numInput.FloatValue = v4.Z;
219 numInput.FloatValue = v4.W;
224 numInput.Flash(GUIStyle.Green);
230 else if (newValue is Color c)
232 for (
int i = 0; i < fields.Length; i++)
234 var field = fields[i];
242 numInput.IntValue = c.R;
245 numInput.IntValue = c.G;
248 numInput.IntValue = c.B;
251 numInput.IntValue = c.A;
256 numInput.Flash(GUIStyle.Green);
262 if (fields.FirstOrDefault() is { } comp && comp.Parent?.Parent?.Parent is { } parent)
264 if (parent.FindChild(
"colorpreview",
true) is
GUIButton preview)
266 preview.Color = preview.HoverColor = preview.PressedColor = preview.SelectedTextColor = c;
272 for (
int i = 0; i < fields.Length; i++)
274 var field = fields[i];
282 numInput.IntValue = r.X;
285 numInput.IntValue = r.Y;
288 numInput.IntValue = r.Width;
291 numInput.IntValue = r.Height;
296 numInput.Flash(GUIStyle.Green);
302 else if (newValue is
string[] a)
304 for (
int i = 0; i < fields.Length; i++)
306 if (i >= a.Length) {
break; }
312 textBox.Flash(GUIStyle.Green);
320 : this(parent, entity, inGame ?
329 elementHeight = (int)(elementHeight * GUI.Scale);
330 var tickBoxStyle = GUIStyle.GetComponentStyle(
"GUITickBox");
331 var textBoxStyle = GUIStyle.GetComponentStyle(
"GUITextBox");
332 var numberInputStyle = GUIStyle.GetComponentStyle(
"GUINumberInput");
333 if (tickBoxStyle.Height.HasValue) { this.elementHeight = Math.Max(tickBoxStyle.Height.Value,
this.elementHeight); }
334 if (textBoxStyle.Height.HasValue) { this.elementHeight = Math.Max(textBoxStyle.Height.Value,
this.elementHeight); }
335 if (numberInputStyle.Height.HasValue) { this.elementHeight = Math.Max(numberInputStyle.Height.Value,
this.elementHeight); }
340 new GUITextBlock(
new RectTransform(
new Point(layoutGroup.Rect.Width,
this.elementHeight), layoutGroup.RectTransform, isFixedSize:
true), entity.
Name, font: titleFont ?? GUIStyle.Font)
342 TextColor = Color.White,
347 List<Header> headers =
new List<Header>()
353 Dictionary<SerializableProperty, Header> propertyHeaders =
new Dictionary<SerializableProperty, Header>();
355 foreach (var property
in properties)
357 var header =
property.GetAttribute<
Header>();
363 if (!headers.Contains(header))
370 propertyHeaders[property] = prevHeader;
374 foreach (
Header header
in headers)
377 foreach (var property
in properties)
379 if (!Equals(propertyHeaders[property], header)) {
continue; }
381 if (header !=
null && !Equals(header, prevHeader))
384 header.
Text, textColor: GUIStyle.TextColorBright, font: GUIStyle.SubHeadingFont);
399 layoutGroup.Recalculate();
412 object value =
property.GetValue(entity);
413 if (property.
PropertyType == typeof(
string) && value ==
null)
418 Identifier propertyTag = $
"{property.PropertyInfo.DeclaringType.Name}.{property.PropertyInfo.Name}".ToIdentifier();
419 Identifier fallbackTag =
property.PropertyInfo.Name.ToIdentifier();
420 LocalizedString displayName = TextManager.Get(propertyTag, $
"sp.{propertyTag}.name".ToIdentifier());
421 if (displayName.IsNullOrEmpty())
424 if (editable !=
null && !
string.IsNullOrEmpty(editable.
FallBackTextTag))
430 displayName = TextManager.Get(fallbackTag, $
"sp.{fallbackTag}.name".ToIdentifier());
434 if (displayName.IsNullOrEmpty())
436 displayName =
property.Name.FormatCamelCaseWithSpaces();
439 if (editable !=
null)
441 if (!MissingLocalizations.Contains($
"sp.{propertyTag}.name|{displayName}"))
443 DebugConsole.NewMessage(
"Missing Localization for property: " + propertyTag);
444 MissingLocalizations.Add($
"sp.{propertyTag}.name|{displayName}");
445 MissingLocalizations.Add($
"sp.{propertyTag}.description|{property.GetAttribute<Serialize>().Description}");
451 LocalizedString toolTip = TextManager.Get($
"sp.{propertyTag}.description");
452 if (entity.GetType() !=
property.PropertyInfo.DeclaringType)
454 Identifier propertyTagForDerivedClass = $
"{entity.GetType().Name}.{property.PropertyInfo.Name}".ToIdentifier();
455 var toolTipForDerivedClass = TextManager.Get($
"{propertyTagForDerivedClass}.description", $
"sp.{propertyTagForDerivedClass}.description");
456 if (!toolTipForDerivedClass.IsNullOrEmpty())
458 toolTip = toolTipForDerivedClass;
461 if (toolTip.IsNullOrEmpty())
463 toolTip = TextManager.Get($
"{propertyTag}.description", $
"{fallbackTag}.description", $
"sp.{fallbackTag}.description");
465 if (toolTip.IsNullOrEmpty())
467 toolTip =
property.GetAttribute<
Serialize>().Description;
471 if (value is
bool boolVal)
473 propertyField =
CreateBoolField(entity, property, boolVal, displayName, toolTip);
475 else if (value.GetType().IsEnum)
477 if (value.GetType().IsDefined(typeof(FlagsAttribute), inherit:
false))
483 propertyField =
CreateEnumField(entity, property, value, displayName, toolTip);
486 else if (value is
int i)
488 propertyField =
CreateIntField(entity, property, i, displayName, toolTip);
490 else if (value is
float f)
492 propertyField =
CreateFloatField(entity, property, f, displayName, toolTip);
494 else if (value is Point p)
496 propertyField =
CreatePointField(entity, property, p, displayName, toolTip);
498 else if (value is Vector2 v2)
502 else if (value is Vector3 v3)
506 else if (value is Vector4 v4)
510 else if (value is Color c)
512 propertyField =
CreateColorField(entity, property, c, displayName, toolTip);
518 else if(value is
string[] a)
522 else if (value is
string or Identifier)
524 propertyField =
CreateStringField(entity, property, value.ToString(), displayName, toolTip);
526 return propertyField;
531 var editableAttribute =
property.GetAttribute<
Editable>();
532 if (editableAttribute.ReadOnly)
534 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
535 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
542 Font = GUIStyle.SmallFont
550 Font = GUIStyle.SmallFont,
554 OnSelected = (tickBox) =>
556 if (SetPropertyValue(property, entity, tickBox.Selected))
558 TrySendNetworkUpdate(entity, property);
561 bool propertyValue = (bool)property.
GetValue(entity);
562 if (tickBox.Selected != propertyValue)
564 tickBox.Selected = propertyValue;
565 tickBox.Flash(Color.Red);
575 return propertyTickBox;
581 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
582 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
586 var editableAttribute =
property.GetAttribute<
Editable>();
588 if (editableAttribute.ReadOnly)
593 Font = GUIStyle.SmallFont
602 Font = GUIStyle.SmallFont,
605 numberInput.MinValueInt = editableAttribute.MinValueInt;
606 numberInput.MaxValueInt = editableAttribute.MaxValueInt;
607 numberInput.IntValue = value;
608 numberInput.OnValueChanged += (numInput) =>
610 if (SetPropertyValue(property, entity, numInput.IntValue))
612 TrySendNetworkUpdate(entity, property);
617 if (!numberInput.TextBox.Selected) { numberInput.IntValue = (int)property.
GetValue(entity); }
627 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent)
631 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
640 Font = GUIStyle.SmallFont
642 var editableAttribute =
property.GetAttribute<
Editable>();
646 numberInput.
ValueStep = editableAttribute.ValueStep;
653 if (SetPropertyValue(property, entity, numInput.FloatValue))
655 TrySendNetworkUpdate(entity, property);
659 HandleSetterValueTampering(numberInput, () => property.
GetFloatValue(entity));
668 private static void HandleSetterValueTampering(
GUINumberInput numberInput, Func<float> getter)
675 var resultingFloatValue = getter();
676 if (!MathUtils.NearlyEqual(resultingFloatValue, inputFloatValue))
681 bool HandleSetterModifyingInputOnButtonPressed() { HandleSetterModifyingInput(numberInput);
return true; }
682 bool HandleSetterModifyingInputOnButtonClicked(GUIButton _,
object __) { HandleSetterModifyingInput(numberInput);
return true; }
693 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, elementHeight), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
694 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
699 elementCount: Enum.GetValues(value.GetType()).Length)
703 foreach (
object enumValue
in Enum.GetValues(value.GetType()))
705 enumDropDown.AddItem(enumValue.ToString(), enumValue);
707 enumDropDown.SelectItem(value);
708 enumDropDown.OnSelected += (selected, val) =>
710 if (SetPropertyValue(property, entity, val))
712 TrySendNetworkUpdate(entity, property);
718 if (!enumDropDown.Dropped) { enumDropDown.SelectItem(property.GetValue(entity)); }
720 if (!
Fields.ContainsKey(property.Name)) {
Fields.Add(property.Name.ToIdentifier(),
new GUIComponent[] { enumDropDown }); }
726 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, elementHeight), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
727 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
732 elementCount: Enum.GetValues(value.GetType()).Length, selectMultiple:
true)
737 bool isFlagsAttribute = value.GetType().IsDefined(typeof(FlagsAttribute),
false);
739 bool hasNoneOption =
false;
740 foreach (
object enumValue
in Enum.GetValues(value.GetType()))
742 if (isFlagsAttribute && !MathHelper.IsPowerOfTwo((
int)enumValue)) {
continue; }
743 hasNoneOption |= (int)enumValue == 0;
744 enumDropDown.AddItem(enumValue.ToString(), enumValue);
745 if (((
int)enumValue != 0 || (
int)value == 0) && ((Enum)value).HasFlag((Enum)enumValue))
747 enumDropDown.SelectItem(enumValue);
750 enumDropDown.MustSelectAtLeastOne = !hasNoneOption;
751 enumDropDown.AfterSelected += (selected, val) =>
753 if (SetPropertyValue(property, entity,
string.Join(
", ", enumDropDown.SelectedDataMultiple.Select(d => d.ToString()))))
755 TrySendNetworkUpdate(entity, property);
760 if (!
Fields.ContainsKey(property.Name)) {
Fields.Add(property.Name.ToIdentifier(),
new GUIComponent[] { enumDropDown }); }
766 bool isItemTagBox = IsItemTagBox(entity, property.
Name, out
Item it);
767 var mainFrame =
new GUILayoutGroup(
new RectTransform(
new Point(
Rect.Width, isItemTagBox ? elementHeight * 2 : elementHeight), layoutGroup.RectTransform, isFixedSize:
true));
769 var frame =
new GUILayoutGroup(
new RectTransform(isItemTagBox ?
new Vector2(1f, 0.5f) : Vector2.One, mainFrame.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
774 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont, textAlignment: Alignment.Left)
779 Identifier translationTextTag =
property.GetAttribute<
Serialize>()?.TranslationTextTag ?? Identifier.Empty;
780 const float browseButtonWidth = 0.1f;
781 var editableAttribute = property.GetAttribute<
Editable>();
782 float textBoxWidth = inputFieldWidth;
783 if (!translationTextTag.IsEmpty || isItemTagBox) { textBoxWidth -= browseButtonWidth; }
786 Enabled = editableAttribute !=
null && !editableAttribute.ReadOnly,
789 Font = GUIStyle.SmallFont,
790 Text = StripPrefabTags(value),
793 if (editableAttribute !=
null && editableAttribute.MaxLength > 0)
798 HashSet<MapEntity> editedEntities =
new HashSet<MapEntity>();
803 editedEntities.Add(entity);
807 propertyBox.
OnDeselected += (textBox, keys) => OnApply(textBox);
811 if (propertyBox.
Selected) {
return; }
812 propertyBox.
Text = StripPrefabTags(property.
GetValue(entity)?.ToString());
820 if (editedEntities.Count > 1)
822 foreach (var entity
in editedEntities)
827 if (SetPropertyValue(property, entity, textBox.
Text))
829 TrySendNetworkUpdate(entity, property);
830 textBox.
Text = StripPrefabTags(property.
GetValue(entity).ToString());
835 foreach (var entity
in prevSelected)
842 if (!translationTextTag.IsEmpty)
844 new GUIButton(
new RectTransform(
new Vector2(browseButtonWidth, 1), frame.RectTransform,
Anchor.TopRight),
"...", style:
"GUIButtonSmall")
846 OnClicked = (bt, userData) => {
CreateTextPicker(translationTextTag.Value, entity, property, propertyBox);
return true; }
851 if (translatedText.IsNullOrEmpty())
854 propertyBox.
ToolTip = TextManager.GetWithVariable(
"StringPropertyCannotTranslate",
"[tag]", text ??
string.Empty);
859 propertyBox.
ToolTip = TextManager.GetWithVariable(
"StringPropertyTranslate",
"[translation]", translatedText);
863 propertyBox.
Text = value;
874 new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), prefabFrame.RectTransform), TextManager.Get(
"predefinedtags.name"), font: GUIStyle.SmallFont, textAlignment: Alignment.Left)
876 ToolTip = TextManager.Get(
"predefinedtags.description")
879 new GUITextBox(
new RectTransform(
new Vector2(inputFieldWidth, 1), prefabFrame.RectTransform), createPenIcon:
false)
882 Font = GUIStyle.SmallFont,
883 Text = GetPrefabTags(it),
885 ToolTip = TextManager.Get(
"predefinedtags.description")
891 OnClicked = (_, _) => { it.CreateContainerTagPicker(propertyBox);
return true; }
901 if (entity is
Item item && propertyName.Equals(nameof(
Item.
Tags), StringComparison.OrdinalIgnoreCase))
910 string StripPrefabTags(
string text)
912 if (!isItemTagBox) {
return text; }
914 string prefabTags = GetPrefabTags(it);
915 if (
string.IsNullOrEmpty(text) ||
string.IsNullOrEmpty(prefabTags)) {
return text; }
917 string[] splitTags = text.Split(
',');
918 return string.Join(
',', splitTags.Where(t => !it.Prefab.Tags.Contains(t)));
921 static string GetPrefabTags(
Item it) =>
string.Join(
',', it.Prefab.Tags);
926 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
927 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
934 RelativeSpacing = 0.05f
936 var editableAttribute =
property.GetAttribute<
Editable>();
938 for (
int i = 1; i >= 0; i--)
940 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.45f, 1), inputArea.RectTransform), style:
null);
943 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
945 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
948 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
952 Font = GUIStyle.SmallFont
960 numberInput.
MinValueInt = editableAttribute.MinValueInt;
961 numberInput.
MaxValueInt = editableAttribute.MaxValueInt;
966 Point newVal = (Point)property.
GetValue(entity);
968 newVal.X = numInput.IntValue;
970 newVal.Y = numInput.IntValue;
972 if (SetPropertyValue(property, entity, newVal))
974 TrySendNetworkUpdate(entity, property);
977 fields[i] = numberInput;
983 Point value = (Point)property.
GetValue(entity);
988 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
995 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
996 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1003 RelativeSpacing = 0.05f
1005 var editableAttribute =
property.GetAttribute<
Editable>();
1007 for (
int i = 1; i >= 0; i--)
1009 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.45f, 1), inputArea.RectTransform), style:
null);
1012 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1014 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1016 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1020 Font = GUIStyle.SmallFont
1023 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1024 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1026 numberInput.
ValueStep = editableAttribute.ValueStep;
1030 numberInput.
FloatValue = i == 0 ? value.X : value.Y;
1035 Vector2 newVal = (Vector2)property.
GetValue(entity);
1038 newVal.X = numInput.FloatValue;
1042 newVal.Y = numInput.FloatValue;
1045 if (SetPropertyValue(property, entity, newVal))
1047 TrySendNetworkUpdate(entity, property);
1050 HandleSetterValueTampering(numberInput, () =>
1052 Vector2 currVal = (Vector2)property.
GetValue(entity);
1053 return comp == 0 ? currVal.X : currVal.Y;
1055 fields[i] = numberInput;
1061 Vector2 value = (Vector2)property.
GetValue(entity);
1066 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1073 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1074 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1081 RelativeSpacing = 0.03f
1083 var editableAttribute =
property.GetAttribute<
Editable>();
1085 for (
int i = 2; i >= 0; i--)
1087 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.33f, 1), inputArea.RectTransform), style:
null);
1090 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1092 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1095 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1099 Font = GUIStyle.SmallFont
1102 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1103 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1105 numberInput.
ValueStep = editableAttribute.ValueStep;
1117 Vector3 newVal = (Vector3)property.
GetValue(entity);
1119 newVal.X = numInput.FloatValue;
1121 newVal.Y = numInput.FloatValue;
1123 newVal.Z = numInput.FloatValue;
1125 if (SetPropertyValue(property, entity, newVal))
1127 TrySendNetworkUpdate(entity, property);
1130 fields[i] = numberInput;
1136 Vector3 value = (Vector3)property.
GetValue(entity);
1142 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1149 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1150 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1154 var editableAttribute =
property.GetAttribute<
Editable>();
1159 RelativeSpacing = 0.01f
1161 for (
int i = 3; i >= 0; i--)
1163 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.22f, 1), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point(150, 50) }, style:
null);
1166 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1168 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1171 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1175 Font = GUIStyle.SmallFont
1178 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1179 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1181 numberInput.
ValueStep = editableAttribute.ValueStep;
1195 Vector4 newVal = (Vector4)property.
GetValue(entity);
1197 newVal.X = numInput.FloatValue;
1199 newVal.Y = numInput.FloatValue;
1201 newVal.Z = numInput.FloatValue;
1203 newVal.W = numInput.FloatValue;
1205 if (SetPropertyValue(property, entity, newVal))
1207 TrySendNetworkUpdate(entity, property);
1210 fields[i] = numberInput;
1216 Vector4 value = (Vector4)property.
GetValue(entity);
1223 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1230 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1231 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform) { MinSize = new Point(80, 26) }, displayName, font: GUIStyle.SmallFont)
1233 ToolTip = displayName +
'\n' + toolTip
1235 label.Text = ToolBox.LimitString(label.Text, label.Font, label.Rect.Width);
1238 AbsoluteOffset = new Point(label.Rect.Width, 0)
1239 },
color: Color.Black, style:
null);
1240 var colorBox =
new GUIButton(
new RectTransform(
new Vector2(largeInputFieldWidth, 0.9f), colorBoxBack.RectTransform,
Anchor.Center), style:
null)
1243 OnClicked = (component, data) =>
1252 var inputArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(Math.Max((frame.Rect.Width - label.Rect.Width - colorBoxBack.Rect.Width) / (
float)frame.Rect.Width, 0.5f), 1), frame.RectTransform,
Anchor.TopRight), isHorizontal:
true, childAnchor:
Anchor.CenterRight)
1255 RelativeSpacing = 0.001f
1258 for (
int i = 3; i >= 0; i--)
1264 new GUITextBlock(
new RectTransform(
new Vector2(0.2f, 1), element.RectTransform,
Anchor.CenterLeft) { MinSize = new Point(15, 0) }, GUI.ColorComponentLabels[i], font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1268 Font = GUIStyle.SmallFont
1282 numberInput.
Font = GUIStyle.SmallFont;
1289 newVal.R = (byte)numInput.IntValue;
1291 newVal.G = (byte)numInput.IntValue;
1293 newVal.B = (byte)numInput.IntValue;
1295 newVal.A = (
byte)numInput.IntValue;
1297 if (SetPropertyValue(property, entity, newVal))
1299 TrySendNetworkUpdate(entity, property);
1300 colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = newVal;
1303 colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = (
Color)property.
GetValue(entity);
1304 fields[i] = numberInput;
1317 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1324 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1325 var label =
new GUITextBlock(
new RectTransform(
new Vector2(0.25f, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1327 ToolTip = displayName +
'\n' + toolTip
1329 label.Text = ToolBox.LimitString(label.Text, label.Font, label.Rect.Width);
1334 RelativeSpacing = 0.01f
1336 for (
int i = 3; i >= 0; i--)
1338 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.22f, 1), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point(150, 50) }, style:
null);
1339 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), GUI.RectComponentLabels[i], font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1343 Font = GUIStyle.SmallFont
1355 numberInput.
IntValue = value.Width;
1357 numberInput.
IntValue = value.Height;
1364 newVal.X = numInput.IntValue;
1366 newVal.Y = numInput.IntValue;
1368 newVal.Width = numInput.IntValue;
1370 newVal.Height = numInput.IntValue;
1372 if (SetPropertyValue(property, entity, newVal))
1374 TrySendNetworkUpdate(entity, property);
1377 fields[i] = numberInput;
1396 int elementCount = (value.Length + 1);
1397 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, elementCount * elementHeight), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1398 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 1.0f / elementCount), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1402 var editableAttribute =
property.GetAttribute<
Editable>();
1404 var inputArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, (
float)(elementCount - 1) / elementCount), frame.RectTransform, anchor:
Anchor.BottomLeft))
1406 RelativeSpacing = 0.01f
1410 for (
int i = 0; i < value.Length; i++)
1412 var element =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 1.0f / elementCount), inputArea.RectTransform) { MinSize = new Point(50, 0), MaxSize = new Point((int)(0.9f * inputArea.Rect.Width), 50) }, style:
null);
1415 string componentLabel = (i + 1).ToString();
1416 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), elementLayoutGroup.RectTransform) { MaxSize = new Point(25, elementLayoutGroup.Rect.Height) }, componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1419 Font = GUIStyle.SmallFont,
1424 textBox.
OnDeselected += (textBox, keys) => OnApply(textBox);
1425 fields[i] = textBox;
1430 bool containsForbiddenCharacters = textBox.
Text.Contains(
';');
1431 string[] newValue = (
string[])property.
GetValue(entity);
1432 if (!containsForbiddenCharacters)
1434 newValue[comp] = textBox.
Text;
1435 if (SetPropertyValue(property, entity, newValue))
1437 TrySendNetworkUpdate(entity, property);
1443 textBox.
Text = newValue[comp];
1452 if (fields.None(f => ((
GUITextBox)f).Selected))
1454 string[] value = (
string[])property.
GetValue(entity);
1455 for (
int i = 0; i < fields.Length; i++)
1462 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Sum(c => c.MinSize.Y));
1470 msgBox.
Buttons[0].OnClicked = msgBox.Close;
1475 OnSelected = (component, userData) =>
1477 string text = userData as
string ??
"";
1479 if (SetPropertyValue(property, entity, text))
1481 TrySendNetworkUpdate(entity, property);
1489 var tagTextPairs = TextManager.GetAllTagTextPairs().ToList();
1490 tagTextPairs.Sort((t1, t2) => {
return t1.Value.CompareTo(t2.Value); });
1491 foreach (KeyValuePair<Identifier, string> tagTextPair
in tagTextPairs)
1493 if (!tagTextPair.Key.StartsWith(textTag)) {
continue; }
1494 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
1495 ToolBox.LimitString(tagTextPair.Value, GUIStyle.Font, textList.Content.Rect.Width))
1497 UserData = tagTextPair.Key.ToString()
1503 foreach (
string extraEntry
in hasExtraTextPickerEntries.GetExtraTextPickerEntries())
1505 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
1506 ToolBox.LimitString(extraEntry, GUIStyle.Font, textList.Content.Rect.Width), GUIStyle.Green)
1518 if (GameMain.Client !=
null)
1520 if (entity is Item item)
1522 GameMain.Client.CreateEntityEvent(item,
new Item.ChangePropertyEventData(property, item));
1526 GameMain.Client.CreateEntityEvent(ic.Item,
new Item.ChangePropertyEventData(property, ic));
1531 private bool SetPropertyValue(SerializableProperty property,
object entity,
object value)
1535 object oldData =
property.GetValue(entity);
1537 if (oldData ==
null && value is
string) { oldData =
""; }
1538 if (entity is ISerializableEntity sEntity && Screen.Selected is SubEditorScreen && !Equals(oldData, value))
1540 List<ISerializableEntity> entities =
new List<ISerializableEntity> { sEntity };
1541 Dictionary<ISerializableEntity, object> affected = MultiSetProperties(property, entity, value);
1543 Dictionary<object, List<ISerializableEntity>> oldValues =
new Dictionary<object, List<ISerializableEntity>> {{ oldData!,
new List<ISerializableEntity> { sEntity }}};
1545 affected.ForEach(aEntity =>
1547 var (item, oldVal) = aEntity;
1550 if (!oldValues.ContainsKey(oldVal))
1552 oldValues.Add(oldVal,
new List<ISerializableEntity> { item });
1556 oldValues[oldVal].Add(item);
1560 PropertyCommand cmd =
new PropertyCommand(entities, property.Name.ToIdentifier(), value, oldValues);
1581 return property.TrySetValue(entity, value);
1605 private Dictionary<ISerializableEntity, object> MultiSetProperties(
SerializableProperty property,
object parentObject,
object value)
1607 Dictionary<ISerializableEntity, object> affected =
new Dictionary<ISerializableEntity, object>();
1610 if (!(parentObject is
ItemComponent || parentObject is Item || parentObject is Structure || parentObject is Hull)) {
return affected; }
1612 foreach (var entity
in MapEntity.SelectedList.Where(entity => entity != parentObject))
1614 switch (parentObject)
1619 if (entity.GetType() == parentObject.GetType())
1621 SafeAdd((ISerializableEntity) entity, property);
1622 property.PropertyInfo.SetValue(entity, value);
1624 else if (entity is ISerializableEntity { SerializableProperties: { } } sEntity)
1626 var props = sEntity.SerializableProperties;
1627 if (props.TryGetValue(property.
Name.ToIdentifier(), out SerializableProperty foundProp) && foundProp.Attributes.OfType<
Editable>().Any())
1629 SafeAdd(sEntity, foundProp);
1630 foundProp.PropertyInfo.SetValue(entity, value);
1635 if (otherItem == parentComponent.Item) {
continue; }
1636 int componentIndex = parentComponent.Item.Components.FindAll(c => c.GetType() == parentComponent.GetType()).IndexOf(parentComponent);
1638 var otherComponents = otherItem.Components.FindAll(c => c.GetType() == parentComponent.GetType());
1639 if (componentIndex >= 0 && componentIndex < otherComponents.Count)
1641 var component = otherComponents[componentIndex];
1642 Debug.Assert(component.GetType() == parentObject.GetType());
1643 SafeAdd(component, property);
1644 if (value is
string stringValue &&
1646 Enum.TryParse(property.
PropertyType, stringValue, out var enumValue))
1648 property.PropertyInfo.SetValue(component, enumValue);
1654 property.PropertyInfo.SetValue(component, value);
1656 catch (ArgumentException e)
1658 DebugConsole.ThrowError($
"Failed to set the value of the property \"{property.Name}\" to {value?.ToString() ?? "null"}", e);
1668 void SafeAdd(ISerializableEntity entity, SerializableProperty prop)
1670 object obj = prop.GetValue(entity);
1671 if (prop.PropertyType == typeof(
string) && obj ==
null) { obj =
string.Empty; }
1672 affected.Add(entity, obj);
virtual bool PlaySoundOnSelect
virtual RichString ToolTip
RectTransform RectTransform
static readonly List< GUIComponent > MessageBoxes
List< GUIButton > Buttons
TextBoxEvent OnDeselected
override RichString ToolTip
OnTextChangedHandler OnTextChanged
Don't set the Text property on delegates that register to this event, because modifying the Text will...
OnEnterHandler OnEnterPressed
override void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectOffset=null)
The base class for components holding the different functionalities of the item
static HashSet< MapEntity > SelectedList
GUIComponent CreateEnumField(ISerializableEntity entity, SerializableProperty property, object value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateStringField(ISerializableEntity entity, SerializableProperty property, string value, LocalizedString displayName, LocalizedString toolTip)
void CreateTextPicker(string textTag, ISerializableEntity entity, SerializableProperty property, GUITextBox textBox)
GUIComponent CreateNewField(SerializableProperty property, ISerializableEntity entity)
GUIComponent CreateBoolField(ISerializableEntity entity, SerializableProperty property, bool value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateIntField(ISerializableEntity entity, SerializableProperty property, int value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateVector2Field(ISerializableEntity entity, SerializableProperty property, Vector2 value, LocalizedString displayName, LocalizedString toolTip)
static Tuple< SerializableProperty, PropertyCommand > CommandBuffer
Dictionary< Identifier, GUIComponent[]> Fields
Holds the references to the input fields.
GUIComponent CreateStringArrayField(ISerializableEntity entity, SerializableProperty property, string[] value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateFloatField(ISerializableEntity entity, SerializableProperty property, float value, LocalizedString displayName, LocalizedString toolTip)
SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, bool inGame, bool showName, string style="", int elementHeight=24, GUIFont titleFont=null)
GUIComponent CreatePointField(ISerializableEntity entity, SerializableProperty property, Point value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateVector4Field(ISerializableEntity entity, SerializableProperty property, Vector4 value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateEnumFlagField(ISerializableEntity entity, SerializableProperty property, object value, LocalizedString displayName, LocalizedString toolTip)
SerializableEntityEditor(RectTransform parent, ISerializableEntity entity, IEnumerable< SerializableProperty > properties, bool showName, string style="", int elementHeight=24, GUIFont titleFont=null)
static bool IsEntityRemoved(object entity)
GUIComponent CreateVector3Field(ISerializableEntity entity, SerializableProperty property, Vector3 value, LocalizedString displayName, LocalizedString toolTip)
void UpdateValue(SerializableProperty property, object newValue, bool flash=true)
static void CommitCommandBuffer()
static bool PropertyChangesActive
GUIComponent CreateColorField(ISerializableEntity entity, SerializableProperty property, Color value, LocalizedString displayName, LocalizedString toolTip)
GUIComponent CreateRectangleField(ISerializableEntity entity, SerializableProperty property, Rectangle value, LocalizedString displayName, LocalizedString toolTip)
static DateTime NextCommandPush
void AddCustomContent(GUIComponent component, int childIndex)
readonly Type PropertyType
float GetFloatValue(object parentObject)
object GetValue(object parentObject)
static GUIMessageBox CreatePropertyColorPicker(Color originalColor, SerializableProperty property, ISerializableEntity entity)
static void StoreCommand(Command command)
static bool IsSubEditor()
string FallBackTextTag
If a translation can't be found for the property name, this tag is used instead
@ Structure
Structures and hulls, but also items (for backwards support)!