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.OnSelected += (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),
794 HashSet<MapEntity> editedEntities =
new HashSet<MapEntity>();
799 editedEntities.Add(entity);
803 propertyBox.
OnDeselected += (textBox, keys) => OnApply(textBox);
807 if (propertyBox.
Selected) {
return; }
809 propertyBox.
Text = StripPrefabTags(property.
GetValue(entity).ToString());
817 if (editedEntities.Count > 1)
819 foreach (var entity
in editedEntities)
824 if (SetPropertyValue(property, entity, textBox.
Text))
826 TrySendNetworkUpdate(entity, property);
827 textBox.
Text = StripPrefabTags(property.
GetValue(entity).ToString());
832 foreach (var entity
in prevSelected)
839 if (!translationTextTag.IsEmpty)
841 new GUIButton(
new RectTransform(
new Vector2(browseButtonWidth, 1), frame.RectTransform,
Anchor.TopRight),
"...", style:
"GUIButtonSmall")
843 OnClicked = (bt, userData) => {
CreateTextPicker(translationTextTag.Value, entity, property, propertyBox);
return true; }
848 if (translatedText.IsNullOrEmpty())
851 propertyBox.
ToolTip = TextManager.GetWithVariable(
"StringPropertyCannotTranslate",
"[tag]", text ??
string.Empty);
856 propertyBox.
ToolTip = TextManager.GetWithVariable(
"StringPropertyTranslate",
"[translation]", translatedText);
860 propertyBox.
Text = value;
871 new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), prefabFrame.RectTransform), TextManager.Get(
"predefinedtags.name"), font: GUIStyle.SmallFont, textAlignment: Alignment.Left)
873 ToolTip = TextManager.Get(
"predefinedtags.description")
876 new GUITextBox(
new RectTransform(
new Vector2(inputFieldWidth, 1), prefabFrame.RectTransform), createPenIcon:
false)
879 Font = GUIStyle.SmallFont,
880 Text = GetPrefabTags(it),
882 ToolTip = TextManager.Get(
"predefinedtags.description")
888 OnClicked = (_, _) => { it.CreateContainerTagPicker(propertyBox);
return true; }
898 if (entity is
Item item && propertyName.Equals(nameof(
Item.
Tags), StringComparison.OrdinalIgnoreCase))
907 string StripPrefabTags(
string text)
909 if (!isItemTagBox) {
return text; }
911 string prefabTags = GetPrefabTags(it);
912 if (
string.IsNullOrEmpty(text) ||
string.IsNullOrEmpty(prefabTags)) {
return text; }
914 string[] splitTags = text.Split(
',');
915 return string.Join(
',', splitTags.Where(t => !it.Prefab.Tags.Contains(t)));
918 static string GetPrefabTags(
Item it) =>
string.Join(
',', it.Prefab.Tags);
923 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
924 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
931 RelativeSpacing = 0.05f
933 var editableAttribute =
property.GetAttribute<
Editable>();
935 for (
int i = 1; i >= 0; i--)
937 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.45f, 1), inputArea.RectTransform), style:
null);
940 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
942 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
945 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
949 Font = GUIStyle.SmallFont
957 numberInput.
MinValueInt = editableAttribute.MinValueInt;
958 numberInput.
MaxValueInt = editableAttribute.MaxValueInt;
963 Point newVal = (Point)property.
GetValue(entity);
965 newVal.X = numInput.IntValue;
967 newVal.Y = numInput.IntValue;
969 if (SetPropertyValue(property, entity, newVal))
971 TrySendNetworkUpdate(entity, property);
974 fields[i] = numberInput;
980 Point value = (Point)property.
GetValue(entity);
985 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
992 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
993 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - inputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1000 RelativeSpacing = 0.05f
1002 var editableAttribute =
property.GetAttribute<
Editable>();
1004 for (
int i = 1; i >= 0; i--)
1006 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.45f, 1), inputArea.RectTransform), style:
null);
1009 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1011 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1013 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1017 Font = GUIStyle.SmallFont
1020 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1021 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1023 numberInput.
ValueStep = editableAttribute.ValueStep;
1027 numberInput.
FloatValue = i == 0 ? value.X : value.Y;
1032 Vector2 newVal = (Vector2)property.
GetValue(entity);
1035 newVal.X = numInput.FloatValue;
1039 newVal.Y = numInput.FloatValue;
1042 if (SetPropertyValue(property, entity, newVal))
1044 TrySendNetworkUpdate(entity, property);
1047 HandleSetterValueTampering(numberInput, () =>
1049 Vector2 currVal = (Vector2)property.
GetValue(entity);
1050 return comp == 0 ? currVal.X : currVal.Y;
1052 fields[i] = numberInput;
1058 Vector2 value = (Vector2)property.
GetValue(entity);
1063 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1070 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1071 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1078 RelativeSpacing = 0.03f
1080 var editableAttribute =
property.GetAttribute<
Editable>();
1082 for (
int i = 2; i >= 0; i--)
1084 var element =
new GUIFrame(
new RectTransform(
new Vector2(0.33f, 1), inputArea.RectTransform), style:
null);
1087 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1089 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1092 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1096 Font = GUIStyle.SmallFont
1099 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1100 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1102 numberInput.
ValueStep = editableAttribute.ValueStep;
1114 Vector3 newVal = (Vector3)property.
GetValue(entity);
1116 newVal.X = numInput.FloatValue;
1118 newVal.Y = numInput.FloatValue;
1120 newVal.Z = numInput.FloatValue;
1122 if (SetPropertyValue(property, entity, newVal))
1124 TrySendNetworkUpdate(entity, property);
1127 fields[i] = numberInput;
1133 Vector3 value = (Vector3)property.
GetValue(entity);
1139 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1146 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1147 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1151 var editableAttribute =
property.GetAttribute<
Editable>();
1156 RelativeSpacing = 0.01f
1158 for (
int i = 3; i >= 0; i--)
1160 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);
1163 if (editableAttribute.VectorComponentLabels !=
null && i < editableAttribute.VectorComponentLabels.Length)
1165 componentLabel = TextManager.Get(editableAttribute.VectorComponentLabels[i]);
1168 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), componentLabel, font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1172 Font = GUIStyle.SmallFont
1175 numberInput.
MinValueFloat = editableAttribute.MinValueFloat;
1176 numberInput.
MaxValueFloat = editableAttribute.MaxValueFloat;
1178 numberInput.
ValueStep = editableAttribute.ValueStep;
1192 Vector4 newVal = (Vector4)property.
GetValue(entity);
1194 newVal.X = numInput.FloatValue;
1196 newVal.Y = numInput.FloatValue;
1198 newVal.Z = numInput.FloatValue;
1200 newVal.W = numInput.FloatValue;
1202 if (SetPropertyValue(property, entity, newVal))
1204 TrySendNetworkUpdate(entity, property);
1207 fields[i] = numberInput;
1213 Vector4 value = (Vector4)property.
GetValue(entity);
1220 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1227 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1228 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f - largeInputFieldWidth, 1), frame.RectTransform) { MinSize = new Point(80, 26) }, displayName, font: GUIStyle.SmallFont)
1230 ToolTip = displayName +
'\n' + toolTip
1232 label.Text = ToolBox.LimitString(label.Text, label.Font, label.Rect.Width);
1235 AbsoluteOffset = new Point(label.Rect.Width, 0)
1236 },
color: Color.Black, style:
null);
1237 var colorBox =
new GUIButton(
new RectTransform(
new Vector2(largeInputFieldWidth, 0.9f), colorBoxBack.RectTransform,
Anchor.Center), style:
null)
1240 OnClicked = (component, data) =>
1249 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)
1252 RelativeSpacing = 0.001f
1255 for (
int i = 3; i >= 0; i--)
1261 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);
1265 Font = GUIStyle.SmallFont
1279 numberInput.
Font = GUIStyle.SmallFont;
1286 newVal.R = (byte)numInput.IntValue;
1288 newVal.G = (byte)numInput.IntValue;
1290 newVal.B = (byte)numInput.IntValue;
1292 newVal.A = (
byte)numInput.IntValue;
1294 if (SetPropertyValue(property, entity, newVal))
1296 TrySendNetworkUpdate(entity, property);
1297 colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = newVal;
1300 colorBox.Color = colorBox.HoverColor = colorBox.PressedColor = colorBox.SelectedTextColor = (
Color)property.
GetValue(entity);
1301 fields[i] = numberInput;
1314 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Max(c => c.MinSize.Y));
1321 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, Math.Max(elementHeight, 26)), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1322 var label =
new GUITextBlock(
new RectTransform(
new Vector2(0.25f, 1), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1324 ToolTip = displayName +
'\n' + toolTip
1326 label.Text = ToolBox.LimitString(label.Text, label.Font, label.Rect.Width);
1331 RelativeSpacing = 0.01f
1333 for (
int i = 3; i >= 0; i--)
1335 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);
1336 new GUITextBlock(
new RectTransform(
new Vector2(0.3f, 1), element.RectTransform,
Anchor.CenterLeft), GUI.RectComponentLabels[i], font: GUIStyle.SmallFont, textAlignment: Alignment.Center);
1340 Font = GUIStyle.SmallFont
1352 numberInput.
IntValue = value.Width;
1354 numberInput.
IntValue = value.Height;
1361 newVal.X = numInput.IntValue;
1363 newVal.Y = numInput.IntValue;
1365 newVal.Width = numInput.IntValue;
1367 newVal.Height = numInput.IntValue;
1369 if (SetPropertyValue(property, entity, newVal))
1371 TrySendNetworkUpdate(entity, property);
1374 fields[i] = numberInput;
1393 int elementCount = (value.Length + 1);
1394 var frame =
new GUIFrame(
new RectTransform(
new Point(
Rect.Width, elementCount * elementHeight), layoutGroup.RectTransform, isFixedSize:
true),
color: Color.Transparent);
1395 var label =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 1.0f / elementCount), frame.RectTransform), displayName, font: GUIStyle.SmallFont)
1399 var editableAttribute =
property.GetAttribute<
Editable>();
1401 var inputArea =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, (
float)(elementCount - 1) / elementCount), frame.RectTransform, anchor:
Anchor.BottomLeft))
1403 RelativeSpacing = 0.01f
1407 for (
int i = 0; i < value.Length; i++)
1409 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);
1412 string componentLabel = (i + 1).ToString();
1413 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);
1416 Font = GUIStyle.SmallFont,
1421 textBox.
OnDeselected += (textBox, keys) => OnApply(textBox);
1422 fields[i] = textBox;
1427 bool containsForbiddenCharacters = textBox.
Text.Contains(
';');
1428 string[] newValue = (
string[])property.
GetValue(entity);
1429 if (!containsForbiddenCharacters)
1431 newValue[comp] = textBox.
Text;
1432 if (SetPropertyValue(property, entity, newValue))
1434 TrySendNetworkUpdate(entity, property);
1440 textBox.
Text = newValue[comp];
1449 if (fields.None(f => ((
GUITextBox)f).Selected))
1451 string[] value = (
string[])property.
GetValue(entity);
1452 for (
int i = 0; i < fields.Length; i++)
1459 frame.RectTransform.MinSize =
new Point(0, frame.RectTransform.Children.Sum(c => c.MinSize.Y));
1467 msgBox.
Buttons[0].OnClicked = msgBox.Close;
1472 OnSelected = (component, userData) =>
1474 string text = userData as
string ??
"";
1476 if (SetPropertyValue(property, entity, text))
1478 TrySendNetworkUpdate(entity, property);
1486 var tagTextPairs = TextManager.GetAllTagTextPairs().ToList();
1487 tagTextPairs.Sort((t1, t2) => {
return t1.Value.CompareTo(t2.Value); });
1488 foreach (KeyValuePair<Identifier, string> tagTextPair
in tagTextPairs)
1490 if (!tagTextPair.Key.StartsWith(textTag)) {
continue; }
1491 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
1492 ToolBox.LimitString(tagTextPair.Value, GUIStyle.Font, textList.Content.Rect.Width))
1494 UserData = tagTextPair.Key.ToString()
1500 foreach (
string extraEntry
in hasExtraTextPickerEntries.GetExtraTextPickerEntries())
1502 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), textList.Content.RectTransform) { MinSize = new Point(0, 20) },
1503 ToolBox.LimitString(extraEntry, GUIStyle.Font, textList.Content.Rect.Width), GUIStyle.Green)
1515 if (GameMain.Client !=
null)
1517 if (entity is Item item)
1519 GameMain.Client.CreateEntityEvent(item,
new Item.ChangePropertyEventData(property, item));
1523 GameMain.Client.CreateEntityEvent(ic.Item,
new Item.ChangePropertyEventData(property, ic));
1528 private bool SetPropertyValue(SerializableProperty property,
object entity,
object value)
1532 object oldData =
property.GetValue(entity);
1534 if (oldData ==
null && value is
string) { oldData =
""; }
1535 if (entity is ISerializableEntity sEntity && Screen.Selected is SubEditorScreen && !Equals(oldData, value))
1537 List<ISerializableEntity> entities =
new List<ISerializableEntity> { sEntity };
1538 Dictionary<ISerializableEntity, object> affected = MultiSetProperties(property, entity, value);
1540 Dictionary<object, List<ISerializableEntity>> oldValues =
new Dictionary<object, List<ISerializableEntity>> {{ oldData!,
new List<ISerializableEntity> { sEntity }}};
1542 affected.ForEach(aEntity =>
1544 var (item, oldVal) = aEntity;
1547 if (!oldValues.ContainsKey(oldVal))
1549 oldValues.Add(oldVal,
new List<ISerializableEntity> { item });
1553 oldValues[oldVal].Add(item);
1557 PropertyCommand cmd =
new PropertyCommand(entities, property.Name.ToIdentifier(), value, oldValues);
1578 return property.TrySetValue(entity, value);
1602 private Dictionary<ISerializableEntity, object> MultiSetProperties(
SerializableProperty property,
object parentObject,
object value)
1604 Dictionary<ISerializableEntity, object> affected =
new Dictionary<ISerializableEntity, object>();
1607 if (!(parentObject is
ItemComponent || parentObject is Item || parentObject is Structure || parentObject is Hull)) {
return affected; }
1609 foreach (var entity
in MapEntity.SelectedList.Where(entity => entity != parentObject))
1611 switch (parentObject)
1616 if (entity.GetType() == parentObject.GetType())
1618 SafeAdd((ISerializableEntity) entity, property);
1619 property.PropertyInfo.SetValue(entity, value);
1621 else if (entity is ISerializableEntity { SerializableProperties: { } } sEntity)
1623 var props = sEntity.SerializableProperties;
1624 if (props.TryGetValue(property.
Name.ToIdentifier(), out SerializableProperty foundProp) && foundProp.Attributes.OfType<
Editable>().Any())
1626 SafeAdd(sEntity, foundProp);
1627 foundProp.PropertyInfo.SetValue(entity, value);
1632 if (otherItem == parentComponent.Item) {
continue; }
1633 int componentIndex = parentComponent.Item.Components.FindAll(c => c.GetType() == parentComponent.GetType()).IndexOf(parentComponent);
1635 var otherComponents = otherItem.Components.FindAll(c => c.GetType() == parentComponent.GetType());
1636 if (componentIndex >= 0 && componentIndex < otherComponents.Count)
1638 var component = otherComponents[componentIndex];
1639 Debug.Assert(component.GetType() == parentObject.GetType());
1640 SafeAdd(component, property);
1641 if (value is
string stringValue &&
1643 Enum.TryParse(property.
PropertyType, stringValue, out var enumValue))
1645 property.PropertyInfo.SetValue(component, enumValue);
1651 property.PropertyInfo.SetValue(component, value);
1653 catch (ArgumentException e)
1655 DebugConsole.ThrowError($
"Failed to set the value of the property \"{property.Name}\" to {value?.ToString() ?? "null"}", e);
1665 void SafeAdd(ISerializableEntity entity, SerializableProperty prop)
1667 object obj = prop.GetValue(entity);
1668 if (prop.PropertyType == typeof(
string) && obj ==
null) { obj =
string.Empty; }
1669 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