2using System.Collections.Generic;
6using HeathenEngineering.SteamworksIntegration;
8using UnityEngine.EventSystems;
19 public ModPreview.PreviewType
kind;
22 private sealed
class HoverRelay : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler
32 private const string urlWorkshopItem =
"https://steamcommunity.com/sharedfiles/filedetails/?id=";
106 private readonly Dictionary<BaseModPackage, string>
searchCache =
new Dictionary<BaseModPackage, string>();
108 private readonly Dictionary<BaseModPackage, PreviewEntry>
previews =
new Dictionary<BaseModPackage, PreviewEntry>();
124 private void Move(
object row,
int a)
127 int num = items.IndexOf(row);
128 if (a > 0 && row is
ModGroup { collapsed:
false } modGroup)
132 if (num < 0 || num + a < 0 || num + a >= items.Count)
137 object obj = items[num + a];
145 if (baseModPackage.group ==
null)
147 num2 = modGroup2 !=
null;
151 else if (baseModPackage.group !=
null)
153 num2 = modGroup2 != baseModPackage.group;
165 baseModPackage.group = ((a > 0) ?
null : modGroup2);
174 private void MoveTo(
object row,
object target)
176 List<BaseModPackage> packages =
manager.packages;
178 List<object> items = this.list.
items;
182 if (target ==
null || target == row || (baseModPackage !=
null && baseModPackage.
builtin) || (modGroup !=
null && modGroup == modGroup2))
188 bool flag = items.IndexOf(row) < items.IndexOf(target);
190 bool flag2 = emptyGroups.Contains(modGroup2);
191 if (
block.Count == 0)
194 emptyGroups.Remove(modGroup);
197 modGroup.after = modGroup2.
after;
198 emptyGroups.Insert(emptyGroups.IndexOf(modGroup2) + (flag ? 1 : 0), modGroup);
202 List<BaseModPackage>
list = ((modGroup2 !=
null) ?
Members(modGroup2) :
null);
208 int num = emptyGroups.IndexOf(modGroup);
209 var (baseModPackage2, num2) = tuple;
210 if (after == baseModPackage2 && num == num2)
221 bool flag3 = modGroup ==
null && flag && (flag2 || (modGroup2 !=
null && !modGroup2.
collapsed));
222 ModGroup modGroup3 = ((modGroup !=
null) ?
block[0].group : ((modGroup2 ==
null) ? baseModPackage.group : (flag3 ? modGroup2 :
null)));
223 if (modGroup ==
null && modGroup3 !=
block[0].group)
227 int num3 = packages.IndexOf(
block[0]);
231 packages.Remove(
item);
233 List<BaseModPackage>
list2 = ((modGroup2 !=
null) ?
Members(modGroup2) :
null);
234 bool flag4 = flag && !flag3;
236 int num4 = ((baseModPackage3 !=
null) ? (packages.IndexOf(baseModPackage3) + (flag4 ? 1 : 0)) : ((flag2 && !
block.Contains(modGroup2.after)) ? ((modGroup2.after !=
null) ? (packages.IndexOf(modGroup2.after) + 1) : packages.Count((
BaseModPackage p) => p.
builtin)) : num3));
237 if (!flag2 && num4 == num3 && modGroup3 ==
block[0].group)
243 packages.InsertRange(num4,
block);
244 BaseModPackage baseModPackage4 = (flag2 ? modGroup2.after :
null);
245 foreach (
ModGroup item2
in emptyGroups)
247 if (
block.Contains(item2.
after) && (num4 != num3 || (flag2 && flag && emptyGroups.IndexOf(item2) <= emptyGroups.IndexOf(modGroup2))))
249 item2.after = after2;
251 else if ((baseModPackage3 !=
null && flag4 && item2.
after == baseModPackage3) || (flag2 && !flag && item2.after == baseModPackage4 && emptyGroups.IndexOf(item2) >= emptyGroups.IndexOf(modGroup2)))
253 item2.after =
block[^1];
256 block[0].group = modGroup3;
282 buttonCollapse.mainText.text = (
Groups().Any((
ModGroup g) => !g.
collapsed) ?
"mod_group_collapse_all" :
"mod_group_expand_all").lang();
283 windows[0].rectBottom.RebuildLayout(recursive:
true);
292 ELayer.config.other.disableMods = on;
300 searchCache[package] = ModSearch.BuildText(package);
303 list.dragViewport =
windows[0].Rect();
304 list.onDragBegin = delegate(
object row)
323 list.dragEdgeSize =
list.
callbacks.GetMold()?.Rect().sizeDelta.y ?? 34f;
333 onInstantiate = delegate(
object row,
ItemMod b)
351 if (emptyGroup.
after ==
null)
353 target.
Add(emptyGroup);
359 if (package.
builtin == builtin && (builtin ||
Match(package)))
363 modGroup = package.
group;
364 if (modGroup !=
null)
366 target.
Add(modGroup);
369 if (
HasFilter || modGroup ==
null || !modGroup.collapsed)
377 if (emptyGroup2.
after == package)
379 target.
Add(emptyGroup2);
387 onDragReorder = delegate(
object row,
int a)
395 List<object> items = target.
items;
396 int num = items.IndexOf(row);
397 MoveTo(row, items[Mathf.Clamp(num + a, 0, items.Count - 1)]);
400 canDragReorder = (
object row) => !(row is
BaseModPackage baseModPackage) || !baseModPackage.builtin
407 string title = ((a.group !=
null && !
HasFilter) ?
" " :
"") + (
ELayer.
core.
mods.packages.IndexOf(a) + 1) +
". " + (a.isInPackages ?
"[Local] " :
"") + a.title.IsEmpty(a.dirInfo.Name);
409 b.buttonActivate.subText.text = a.version;
410 b.buttonLock.mainText.text = a.author;
438 bt.SetOnClick(delegate
440 a.willActivate = !a.willActivate;
446 SetGroupRow(a.group, list.GetPair<ItemMod>(a.group));
449 bt.interactable = !a.builtin;
458 uIContextMenu.
AddButton(
"mod_publish", delegate
462 Dialog.
YesNo(
"mod_publish_warn".lang(a.title, a.id, a.author), delegate
464 ELayer.core.steam.CreateUserContent(a);
469 uIContextMenu.
AddButton(a.willActivate ?
"mod_deactivate" :
"mod_activate", delegate
472 a.willActivate = !a.willActivate;
473 ELayer.core.mods.SaveLoadOrder();
475 textRestart.SetActive(enable: true);
477 if (!a.isInPackages && !a.workshopId.IsEmpty())
479 uIContextMenu.
AddButton(
"mod_convert_local", delegate
482 string path = (
"Mod_" + a.workshopId +
"_" + a.id).SanitizeDirectoryName();
486 modPackage.UpdateMeta(updateOnly:
true);
487 searchCache[modPackage] = ModSearch.BuildText(modPackage);
489 modPackage.group = a.group;
490 modPackage.willActivate =
false;
491 modPackage.activated =
false;
497 if (a.isInPackages && a.IsSourceLocalizable)
499 uIContextMenu.
AddButton(
"mod_export_text", delegate
510 uIContextMenu.
AddButton(
"config", delegate
516 if (ModPreview.FindPreviewFile(a) !=
null || (
bool)
SteamPreview(a))
518 uIContextMenu.
AddButton(
"mod_preview", delegate
523 if (!a.workshopId.IsEmpty())
525 uIContextMenu.
AddButton(
"mod_info_workshop", delegate
528 Application.OpenURL(
"https://steamcommunity.com/sharedfiles/filedetails/?id=" + a.workshopId.Trim());
533 uIContextMenu.
AddButton(
"mod_id_copy", delegate
536 GUIUtility.systemCopyBuffer = a.id;
537 ELayer.
ui.Say(
"mod_id_copied".lang(a.id));
540 uIContextMenu.
AddButton(
"mod_info", delegate
543 if (!a.dirInfo.Exists)
550 string text = a.dirInfo.FullName +
"/package.xml";
551 Util.ShowExplorer(File.Exists(text) ? text : a.dirInfo.FullName);
554 uIContextMenu.
Show();
566 return (from p in
manager.packages
567 select p.@group into g
596 List<BaseModPackage> l =
manager.packages;
608 InputGroupName(
"mod_group_default_name".lang(), delegate(
string name)
622 list.dragScrollView.verticalNormalizedPosition = 0f;
628 List<BaseModPackage> members =
Members(g);
630 string title = ((members.Count == 0) ?
"" : (g.
collapsed ?
"[+] " :
"[-] ")) + g.
name;
632 mainText.fontStyle = FontStyle.Bold;
634 Color topic = mainText.color;
638 Color valueOrDefault = color.GetValueOrDefault();
639 mainText.SetText(mainText.text, valueOrDefault.Multiply(0.45f, 0f));
642 b.imageGroup.color = (g.color ?? topic).SetAlpha(0.4f);
643 b.buttonActivate.subText.text =
"";
644 b.buttonLock.mainText.text =
"";
654 b.buttonToggle.interactable = members.Count > 0;
659 item.willActivate = on != members.Count;
683 g.color = ((c == topic) ? ((
Color?)
null) :
new Color?(c));
710 InputGroupName(
"mod_group_default_name".lang(), delegate(
string name)
718 List<ModGroup> groups = (from g in
Groups()
721 if (groups.Count > 0)
726 Dialog.List(
"mod_group_select", groups, (
ModGroup g) => g.
name, delegate(
int i,
string _)
748 List<BaseModPackage> packages =
manager.packages;
756 int num = packages.IndexOf(p);
759 int num2 = ((!emptyGroups.Contains(g)) ? ((baseModPackage !=
null) ? (packages.IndexOf(baseModPackage) + 1) : num) : ((g.after == p) ? num : ((g.
after !=
null) ? (packages.IndexOf(g.
after) + 1) : packages.Count((
BaseModPackage m) => m.
builtin))));
760 packages.Insert(num2, p);
761 BaseModPackage baseModPackage2 = (emptyGroups.Contains(g) ? g.after :
null);
764 if (
item.after == p && num2 != num)
768 else if (emptyGroups.Contains(g) && item.after == baseModPackage2 && emptyGroups.IndexOf(
item) >= emptyGroups.IndexOf(g))
792 item.collapsed = collapsed;
799 Dialog.
InputName(
"mod_group_name", text, delegate(
bool cancel,
string s)
803 string text2 = (s ??
"").Trim();
818 TextGenerationSettings generationSettings = text.GetGenerationSettings(Vector2.zero);
819 TextGenerator cachedTextGeneratorForLayout = text.cachedTextGeneratorForLayout;
820 float num = text.rectTransform.rect.width * text.pixelsPerUnit;
821 if (cachedTextGeneratorForLayout.GetPreferredWidth(
title + suffix, generationSettings) <= num)
823 return title + suffix;
825 int num2 =
title.Length;
826 while (num2 > 1 && cachedTextGeneratorForLayout.GetPreferredWidth(
title[..num2].TrimEnd() +
".." + suffix, generationSettings) > num)
830 return title[..num2].TrimEnd() +
".." + suffix;
849 text.text =
"mod_search".lang();
913 windows[0].rectBottom.RebuildLayout(recursive:
true);
921 ModSearch.Filter.Enabled =>
"mod_filter_enabled",
922 ModSearch.Filter.Disabled =>
"mod_filter_disabled",
923 ModSearch.Filter.Problems =>
"mod_filter_problem",
924 ModSearch.Filter.Local =>
"mod_filter_local",
925 ModSearch.Filter.Workshop =>
"mod_filter_workshop",
945 EventSystem current = EventSystem.current;
948 current.SetSelectedGameObject(
null);
956 if (
previews.TryGetValue(p, out var value))
965 Texture2D texture2D2 = ModPreview.Downscale(texture2D, 280, owns);
966 value.sprite = texture2D2.ToSprite();
967 value.ownsTexture = owns || texture2D2 != texture2D;
975 kind = ModPreview.PreviewType.None;
977 FileInfo fileInfo = ModPreview.FindPreviewFile(p);
978 if (fileInfo !=
null)
982 Texture2D texture2D = ModPreview.LoadTexture(File.ReadAllBytes(fileInfo.FullName), out kind, mipmap);
990 Debug.LogWarning(
"#mod preview " + fileInfo.FullName +
": " + ex.Message);
994 if ((
bool)texture2D2)
997 if (kind == ModPreview.PreviewType.None)
999 kind = ModPreview.PreviewType.Jpeg;
1007 if (!(p.
item is WorkshopItem workshopItem) || !workshopItem.previewImage || workshopItem.previewImage.width <= 2)
1011 return workshopItem.previewImage;
1028 TextGenerationSettings generationSettings = text.GetGenerationSettings(
new Vector2(text.rectTransform.rect.width, 0f));
1029 generationSettings.horizontalOverflow = HorizontalWrapMode.Wrap;
1030 TextGenerator cachedTextGeneratorForLayout = text.cachedTextGeneratorForLayout;
1031 cachedTextGeneratorForLayout.Populate(text.text, generationSettings);
1032 if (cachedTextGeneratorForLayout.lineCount < 2)
1036 if (cachedTextGeneratorForLayout.lineCount > 2)
1038 string text2 = text.text;
1039 int num = cachedTextGeneratorForLayout.lines[2].startCharIdx;
1042 int length = --num - 0;
1043 string str = (text.text = text2.Substring(0, length).TrimEnd() +
"..");
1044 cachedTextGeneratorForLayout.Populate(str, generationSettings);
1046 while (num > 1 && cachedTextGeneratorForLayout.lineCount > 2);
1052 Vector2 size = image.sprite.rect.size;
1053 infoPreview.image2.rectTransform.sizeDelta = size * Mathf.Min(image.rectTransform.rect.width / size.x, image.rectTransform.rect.height / size.y) + Vector2.one * 18f;
1057 string text4 = ModLoadOrderPreset.WorkshopId(p);
1058 SetTopic(
infoWorkshop, text4.IsEmpty() ? p.
dirInfo.Name : text4, text4.IsEmpty() ?
"mod_filter_local" :
"mod_filter_workshop");
1060 List<string>
list =
new List<string>();
1068 list.
Add(array[0].lang(
string.Join(
"mod_info_or".lang(), array, 1, array.Length - 1)));
1084 string[] tags = p.
tags;
1085 SetTopic(
item, (tags !=
null && tags.Length > 0) ?
string.Join(
", ", p.
tags) :
null,
null);
1093 panelScroll.content.anchoredPosition = Vector2.zero;
1094 static string IdList(
string[][] rows,
bool alternatives)
1100 string sep = (alternatives ?
"mod_info_or".lang() :
"\n");
1101 return string.Join(
"\n", from row in rows
1102 where row !=
null && row.Length > 0
1103 select
string.Join(sep, row.Where((
string id) => !
id.IsEmpty())) into line
1104 where !line.IsEmpty()
1107 static void SetInfo(
UIItem uIItem,
string value)
1109 uIItem.SetActive(!value.IsEmpty());
1110 if (!value.IsEmpty())
1115 static void SetTopic(
UIItem uIItem,
string value,
string lang)
1117 uIItem.SetActive(!value.IsEmpty());
1118 if (!value.IsEmpty())
1132 ModPreview.PreviewType kind;
1138 ELayer.
ui.Say(
"mod_preview_unsupported");
1141 Sprite image = tex.ToSprite();
1146 UnityEngine.Object.Destroy(tex);
1149 UnityEngine.Object.Destroy(tex);
1164 uIContextMenu.
AddButton(
"mod_preset_load", delegate
1168 uIContextMenu.
AddButton(
"mod_preset_delete", delegate
1174 uIContextMenu.
AddButton(
"mod_preset_folder", delegate
1179 uIContextMenu.
Show();
1184 Dialog.
InputName(
"mod_preset_name",
"mod_preset_default_name".lang(), delegate(
bool cancel,
string text)
1188 string name = (text ??
"").Trim().SanitizeFileName().Trim();
1195 Dialog.
YesNo(
"mod_preset_overwrite".lang(name), delegate
1216 ELayer.
ui.Say(
"mod_preset_saved".lang(name));
1222 if (files.Count == 0)
1227 Dialog.List(
"mod_preset_select", files, (FileInfo f) => Path.GetFileNameWithoutExtension(f.Name), delegate(
int i,
string _)
1231 }, canCancel:
true);
1248 string name = Path.GetFileNameWithoutExtension(file.Name);
1249 Dialog.
YesNo(
"mod_preset_delete_confirm".lang(name), delegate
1256 ELayer.
ui.Say(
"mod_preset_deleted".lang(name));
1263 if (preset.entries.Count == 0)
1268 GUIUtility.systemCopyBuffer = ModLoadOrderPreset.Serialize(preset);
1270 ELayer.
ui.Say(
"mod_preset_copied");
1275 if (!ModLoadOrderPreset.TryParse(GUIUtility.systemCopyBuffer, out var preset, out var _))
1280 if (preset.name.IsEmpty())
1282 preset.name =
"mod_preset_default_name".lang();
1289 List<ModLoadOrderPreset.Entry>
list = ModLoadOrderPreset.FindMissing(
manager.packages, preset);
1290 if (
list.Count == 0)
1298 },
null,
"mod_preset_apply",
"cancel");
1303 StringBuilder stringBuilder =
new StringBuilder();
1304 for (
int i = 0; i < entries.Count && i < max; i++)
1306 ModLoadOrderPreset.Entry entry = entries[i];
1307 stringBuilder.Append(entry.title.IsEmpty() ? entry.id : (entry.title +
" (" + entry.id +
")")).Append(
'\n');
1309 if (entries.Count > max)
1311 stringBuilder.Append(
"mod_preset_more".lang((entries.Count - max).ToString() ??
"")).Append(
'\n');
1313 return stringBuilder.ToString().TrimEnd();
1327 if (!preset.gameVersion.IsEmpty() && preset.gameVersion != text)
1329 ELayer.
ui.Say(
"mod_preset_version_warn".lang(preset.gameVersion, text));
1331 ELayer.
ui.Say(
"mod_preset_applied".lang(preset.name, applyResult.enabled.ToString() ??
"", applyResult.missing.Count.ToString() ??
""));
1350 UnityEngine.Object.Destroy(value.
sprite.texture);
1352 UnityEngine.Object.Destroy(value.
sprite);
UIList.ICallback callbacks
BaseModPackage duplicateOf
void UpdateMeta(bool updateOnly=false)
static string PathLoadOrderPreset
static void TryWarnUpload(Action action)
static Dialog YesNo(string langDetail, Action actionYes, Action actionNo=null, string langYes="yes", string langNo="no")
static Dialog InputName(string langDetail, string text, Action< bool, string > onClose, InputType inputType=InputType.Default)
static Dialog Ok(string langDetail, Action action=null)
void SetImage(Sprite sprite)
static void Open(ModPackage p)
void OnPointerEnter(PointerEventData eventData)
ModPreview.PreviewType kind
void AddGroupButtons(UIContextMenu m, BaseModPackage a)
const string urlWorkshopItem
IEnumerable< ModGroup > Groups()
void MoveTo(object row, object target)
void ShowInfo(BaseModPackage p)
void InputGroupName(string text, Action< string > onInput)
void SelectPreset(Action< FileInfo > onSelect)
bool Match(BaseModPackage p)
UIButton buttonClearSearch
static Texture2D LoadPreviewTexture(BaseModPackage p, bool mipmap, out ModPreview.PreviewType kind, out bool owns)
int GetGroupIndex(BaseModPackage anchor, bool last)
void RefreshFilterButton()
ModSearch.Filter filterMode
UIButton toggleDisableMods
void ApplyPreset(ModLoadOrderPreset.Preset preset)
PreviewEntry GetThumb(BaseModPackage p)
BaseModPackage panelTarget
void OnReorder(bool restart=true)
void ShowPreview(BaseModPackage a)
static string FilterLabel(ModSearch.Filter f)
static Texture2D SteamPreview(BaseModPackage p)
void Move(object row, int a)
void SetModRow(ModPackage a, ItemMod b)
UIList.Callback< object, ItemMod > CreateCallbacks(UIList target, bool builtin)
void ApplyPresetFile(FileInfo file)
static string FitTitle(UIText text, string title, string suffix="")
readonly Dictionary< BaseModPackage, string > searchCache
void WritePreset(string name)
static string DescribeEntries(List< ModLoadOrderPreset.Entry > entries, int max)
void SetGroup(BaseModPackage p, ModGroup g)
readonly Dictionary< BaseModPackage, PreviewEntry > previews
void DeletePreset(FileInfo file)
void SetGroupRow(ModGroup g, ItemMod b)
void ConfirmPreset(ModLoadOrderPreset.Preset preset)
BaseModPackage FirstInfoTarget()
List< BaseModPackage > Members(ModGroup g)
void SetMembersActive(ModGroup g, bool active)
void LeaveGroup(BaseModPackage p)
Layer SetOnKill(Action action)
bool TryLoadPresetFile(FileInfo file, out ModLoadOrderPreset.Preset preset, out string error)
ModLoadOrderPreset.ApplyResult ApplyPreset(ModLoadOrderPreset.Preset preset, bool disableOthers=true)
static string GetPresetPath(string name)
ModPackage AddPackage(DirectoryInfo dir, bool isInPackages=false)
readonly List< ModGroup > emptyGroups
bool SavePreset(string name, out string path)
List< FileInfo > ListPresets()
void CopyContentTo(string dir)
override string UpdateSourceLocalizationFile(string lang, bool force=false)
Action< UINote > onBuildConfig
UIScrollView dragScrollView
override void Add(object item)
ButtonPair GetPair(object item)