2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
12 public static class FileSelection
14 private static bool open;
15 public static bool Open
23 if (value) { InitIfNecessary(); }
26 fileSystemWatcher?.Dispose();
27 fileSystemWatcher =
null;
33 private static GUIFrame? backgroundFrame;
34 private static GUIFrame? window;
35 private static GUIListBox? sidebar;
36 private static GUIListBox? fileList;
37 private static GUITextBox? directoryBox;
38 private static GUITextBox? filterBox;
39 private static GUITextBox? fileBox;
40 private static GUIDropDown? fileTypeDropdown;
41 private static GUIButton? openButton;
43 private static System.IO.FileSystemWatcher? fileSystemWatcher;
45 private enum ItemIsDirectory
50 private static string? currentFileTypePattern;
52 private static readonly
string[] ignoredDrivePrefixes =
57 private static string currentDirectory =
"";
58 public static string CurrentDirectory
62 return currentDirectory;
66 string[] dirSplit = value.Replace(
'\\',
'/').Split(
'/');
67 List<string> dirs =
new List<string>();
68 for (
int i = 0; i < dirSplit.Length; i++)
70 if (dirSplit[i].Trim() ==
"..")
74 dirs.RemoveAt(dirs.Count - 1);
77 else if (dirSplit[i].Trim() !=
".")
79 dirs.Add(dirSplit[i]);
82 currentDirectory =
string.Join(
"/", dirs);
83 if (!currentDirectory.EndsWith(
"/"))
85 currentDirectory +=
"/";
87 fileSystemWatcher?.Dispose();
88 fileSystemWatcher =
new System.IO.FileSystemWatcher(currentDirectory)
91 NotifyFilter = System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName
93 fileSystemWatcher.Created += OnFileSystemChanges;
94 fileSystemWatcher.Deleted += OnFileSystemChanges;
95 fileSystemWatcher.Renamed += OnFileSystemChanges;
96 fileSystemWatcher.EnableRaisingEvents =
true;
101 public static Action<string>? OnFileSelected
107 private static void OnFileSystemChanges(
object sender, System.IO.FileSystemEventArgs e)
109 if (fileList is
null) {
return; }
110 switch (e.ChangeType)
112 case System.IO.WatcherChangeTypes.Created:
114 var itemFrame =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name ??
string.Empty)
116 UserData = Directory.Exists(e.FullPath) ? ItemIsDirectory.Yes : ItemIsDirectory.No
118 if (itemFrame.UserData is ItemIsDirectory.Yes)
120 itemFrame.Text +=
"/";
122 fileList.Content.RectTransform.SortChildren(SortFiles);
125 case System.IO.WatcherChangeTypes.Deleted:
127 var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == e.Name || tb.Text == e.Name +
"/"));
128 if (itemFrame !=
null) { fileList.RemoveChild(itemFrame); }
131 case System.IO.WatcherChangeTypes.Renamed:
133 System.IO.RenamedEventArgs renameArgs = e as System.IO.RenamedEventArgs ??
throw new InvalidCastException($
"Unable to cast {nameof(System.IO.FileSystemEventArgs)} to {nameof(System.IO.RenamedEventArgs)}.");
135 fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName +
"/")) as GUITextBlock
136 ??
throw new Exception($
"Could not find file list item with name \"{renameArgs.OldName}\"");
137 itemFrame.UserData = Directory.Exists(e.FullPath) ? ItemIsDirectory.Yes : ItemIsDirectory.No;
138 itemFrame.Text = renameArgs.Name ??
string.Empty;
139 if (itemFrame.UserData is ItemIsDirectory.Yes)
141 itemFrame.Text +=
"/";
143 fileList.Content.RectTransform.SortChildren(SortFiles);
149 private static int SortFiles(RectTransform r1, RectTransform r2)
151 string file1 = (r1.GUIComponent as GUITextBlock)?.Text?.SanitizedValue ??
"";
152 string file2 = (r2.GUIComponent as GUITextBlock)?.Text?.SanitizedValue ??
"";
153 bool dir1 = r1.GUIComponent.UserData is ItemIsDirectory.Yes;
154 bool dir2 = r2.GUIComponent.UserData is ItemIsDirectory.Yes;
159 else if (!dir1 && dir2)
164 return string.Compare(file1, file2, StringComparison.OrdinalIgnoreCase);
167 private static void InitIfNecessary()
169 if (backgroundFrame ==
null) { Init(); }
172 public static void Init()
174 backgroundFrame =
new GUIFrame(
new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas), style:
null)
176 Color = Color.Black * 0.5f,
177 HoverColor = Color.Black * 0.5f,
178 SelectedColor = Color.Black * 0.5f,
179 PressedColor = Color.Black * 0.5f,
182 window =
new GUIFrame(
new RectTransform(Vector2.One * 0.8f, backgroundFrame.RectTransform,
Anchor.Center));
184 var horizontalLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One * 0.9f, window.RectTransform,
Anchor.Center),
true);
185 sidebar =
new GUIListBox(
new RectTransform(
new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform))
187 PlaySoundOnSelect =
true
190 var drives = System.IO.DriveInfo.GetDrives();
191 foreach (var drive
in drives)
193 if (drive.DriveType == System.IO.DriveType.Ram) {
continue; }
194 if (ignoredDrivePrefixes.Any(p => drive.Name.StartsWith(p))) {
continue; }
195 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), sidebar.Content.RectTransform), drive.Name.Replace(
'\\',
'/'));
198 sidebar.OnSelected = (child, userdata) =>
200 CurrentDirectory = (child as GUITextBlock)?.Text.SanitizedValue ??
throw new Exception(
"Sidebar selection is invalid");
206 new GUIFrame(
new RectTransform(
new Vector2(0.01f, 1.0f), horizontalLayout.RectTransform), style:
null);
208 var fileListLayout =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.7f, 1.0f), horizontalLayout.RectTransform));
209 var firstRow =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.04f), fileListLayout.RectTransform), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
210 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1.0f), firstRow.RectTransform),
"^")
212 OnClicked = MoveToParentDirectory
214 directoryBox =
new GUITextBox(
new RectTransform(
new Vector2(0.7f, 1.0f), firstRow.RectTransform))
217 OnEnterPressed = (tb, txt) =>
219 if (Directory.Exists(txt))
221 CurrentDirectory = txt;
226 tb.Text = CurrentDirectory;
231 filterBox =
new GUITextBox(
new RectTransform(
new Vector2(0.25f, 1.0f), firstRow.RectTransform))
235 firstRow.RectTransform.MinSize =
new Point(0, firstRow.RectTransform.Children.Max(c => c.MinSize.Y));
237 filterBox.OnTextChanged += (txtbox, txt) =>
243 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style:
null);
245 fileList =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.85f), fileListLayout.RectTransform))
247 PlaySoundOnSelect =
true,
248 OnSelected = (child, userdata) =>
250 if (userdata is
null) {
return false; }
251 if (fileBox is
null) {
return false; }
253 var fileName = (child as GUITextBlock)!.Text.SanitizedValue;
254 fileBox.Text = fileName;
255 if (PlayerInput.DoubleClicked())
257 bool isDir = userdata is ItemIsDirectory.Yes;
260 CurrentDirectory += fileName;
264 OnFileSelected?.Invoke(CurrentDirectory + fileName);
274 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style:
null);
276 var thirdRow =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.04f), fileListLayout.RectTransform),
true);
277 fileBox =
new GUITextBox(
new RectTransform(
new Vector2(0.7f, 1.0f), thirdRow.RectTransform))
279 OnEnterPressed = (tb, txt) => openButton?.OnClicked?.Invoke(openButton,
null) ??
false
282 fileTypeDropdown =
new GUIDropDown(
new RectTransform(
new Vector2(0.3f, 1.0f), thirdRow.RectTransform), dropAbove:
true)
284 OnSelected = (child, userdata) =>
286 currentFileTypePattern = (child as GUITextBlock)!.UserData as
string;
293 fileTypeDropdown.Select(4);
296 new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style:
null);
297 var fourthRow =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.04f), fileListLayout.RectTransform),
true);
300 new GUIFrame(
new RectTransform(
new Vector2(0.7f, 1.0f), fourthRow.RectTransform), style:
null);
302 openButton =
new GUIButton(
new RectTransform(
new Vector2(0.15f, 1.0f), fourthRow.RectTransform), TextManager.Get(
"opensubbutton"))
304 OnClicked = (btn, obj) =>
306 if (Directory.Exists(Path.Combine(CurrentDirectory, fileBox.Text)))
308 CurrentDirectory += fileBox.Text;
310 if (!File.Exists(CurrentDirectory + fileBox.Text)) {
return false; }
311 OnFileSelected?.Invoke(CurrentDirectory + fileBox.Text);
316 new GUIButton(
new RectTransform(
new Vector2(0.15f, 1.0f), fourthRow.RectTransform), TextManager.Get(
"cancel"))
318 OnClicked = (btn, obj) =>
325 CurrentDirectory = Directory.GetCurrentDirectory();
328 public static void ClearFileTypeFilters()
331 fileTypeDropdown!.ClearChildren();
334 public static void AddFileTypeFilter(
string name,
string pattern)
337 fileTypeDropdown!.AddItem(name +
" (" + pattern +
")", pattern);
340 public static void SelectFileTypeFilter(
string pattern)
343 fileTypeDropdown!.SelectItem(pattern);
346 public static void RefreshFileList()
349 fileList!.Content.ClearChildren();
350 fileList.BarScroll = 0.0f;
354 var directories = Directory.EnumerateDirectories(currentDirectory,
"*" + filterBox!.Text +
"*");
355 foreach (var directory
in directories)
357 string txt = directory;
358 if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
359 if (!txt.EndsWith(
"/")) { txt +=
"/"; }
365 Directory.GetDirectories(directory);
367 catch (UnauthorizedAccessException)
371 var itemFrame =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
373 UserData = ItemIsDirectory.Yes
375 var folderIcon =
new GUIImage(
new RectTransform(
new Point((
int)(itemFrame.Rect.Height * 0.8f)), itemFrame.RectTransform,
Anchor.CenterLeft)
377 AbsoluteOffset = new Point((int)(itemFrame.Rect.Height * 0.25f), 0)
378 }, style:
"OpenButton", scaleToFit:
true);
379 itemFrame.Padding =
new Vector4(folderIcon.Rect.Width * 1.5f, itemFrame.Padding.Y, itemFrame.Padding.Z, itemFrame.Padding.W);
382 IEnumerable<string> files = Enumerable.Empty<
string>();
383 if (currentFileTypePattern.IsNullOrEmpty())
385 files = Directory.GetFiles(currentDirectory);
389 foreach (
string pattern
in currentFileTypePattern!.Split(
','))
391 string patternTrimmed = pattern.Trim();
392 patternTrimmed =
"*" + filterBox.Text +
"*" + patternTrimmed;
395 files = Directory.EnumerateFiles(currentDirectory, patternTrimmed);
399 files = files.Concat(Directory.EnumerateFiles(currentDirectory, patternTrimmed));
404 foreach (var file
in files)
407 if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
408 var itemFrame =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
410 UserData = ItemIsDirectory.No
416 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.05f), fileList.Content.RectTransform),
"Could not list items in directory: " + e.Message)
422 fileList.Content.RectTransform.SortChildren(SortFiles);
424 directoryBox!.Text = currentDirectory;
429 public static bool MoveToParentDirectory(GUIButton button,
object userdata)
431 string dir = CurrentDirectory;
432 if (dir.EndsWith(
"/")) { dir = dir.Substring(0, dir.Length - 1); }
433 int index = dir.LastIndexOf(
"/");
434 if (index < 0) {
return false; }
435 CurrentDirectory = CurrentDirectory.Substring(0, index + 1);
440 public static void AddToGUIUpdateList()
442 if (!Open) {
return; }
443 backgroundFrame?.AddToGUIUpdateList();