thief-mission-viewer/project/code/TMV/UI/TextureBrowser.cs

189 lines
5.3 KiB
C#
Raw Normal View History

2024-09-08 18:18:44 +00:00
using System;
2024-09-08 16:53:52 +00:00
using System.Collections.Generic;
2024-09-08 18:24:21 +00:00
using System.IO;
2024-09-08 16:22:47 +00:00
using System.Linq;
using System.Text.RegularExpressions;
using Godot;
namespace KeepersCompound.TMV.UI;
public partial class TextureBrowser : Node
{
2024-09-08 16:22:47 +00:00
enum SortMode
{
Name,
Family,
Index,
Count,
}
private Tree _folderTree;
private LineEdit _searchBar;
private MenuButton _sortMenu;
private Button _openFolderButton;
private MenuButton _browserMenu;
private HFlowContainer _textureList;
private TextureRect _previewTexture;
private LineEdit _texturePath;
2024-09-08 18:18:44 +00:00
private OptionButton _filterOptions;
2024-09-08 18:24:21 +00:00
private LineEdit _fileType;
2024-09-08 17:29:58 +00:00
private string _searchFilterPrefix = "";
public override void _Ready()
{
// !HACK TEMP
Context.Instance.PathManager.Init("/stuff/Games/thief/drive_c/GOG Games/TG ND 1.27 (MAPPING)/");
2024-09-08 18:18:44 +00:00
Context.Instance.CampaignName = "TheBlackParade_1_0";
_folderTree = GetNode<Tree>("%FolderTree");
_searchBar = GetNode<LineEdit>("%SearchBar");
_sortMenu = GetNode<MenuButton>("%SortMenu");
_openFolderButton = GetNode<Button>("%OpenTextureFolderButton");
_browserMenu = GetNode<MenuButton>("%BrowserOverflowMenu");
_textureList = GetNode<HFlowContainer>("%TextureList");
_previewTexture = GetNode<TextureRect>("%PreviewTexture");
_texturePath = GetNode<LineEdit>("%PathBox");
2024-09-08 18:18:44 +00:00
_filterOptions = GetNode<OptionButton>("%FilterOptions");
2024-09-08 18:24:21 +00:00
_fileType = GetNode<LineEdit>("%FileTypeBox");
_searchBar.TextChanged += ApplySearchFilter;
2024-09-08 16:22:47 +00:00
_sortMenu.GetPopup().IdPressed += ApplySortMode;
2024-09-08 17:29:58 +00:00
_folderTree.ItemSelected += SetActiveFolder;
2024-09-08 18:18:44 +00:00
_filterOptions.ItemSelected += SetPreviewTextureFilter;
BuildFolderTree();
BuildTextureList(); // TODO: This should be triggered on folder change
}
private void BuildFolderTree()
{
_folderTree.Clear();
2024-09-08 16:53:52 +00:00
var context = Context.Instance;
var textureNames = context.PathManager.GetResourceNames(LGS.ResourceType.Texture, context.CampaignName);
var treeItems = new Dictionary<string, TreeItem>();
treeItems.Add("", _folderTree.CreateItem());
foreach (var name in textureNames)
{
var tokens = name.Split('/');
var fams = new string[tokens.Length + 1];
fams[0] = "";
for (var i = 1; i < tokens.Length; i++)
{
fams[i] = tokens[..i].Join("/");
if (!treeItems.ContainsKey(fams[i]))
{
var item = _folderTree.CreateItem(treeItems[fams[i - 1]]);
item.SetText(0, tokens[i - 1]);
treeItems.Add(fams[i], item);
}
}
}
}
private void BuildTextureList()
{
foreach (var child in _textureList.GetChildren())
{
child.QueueFree();
}
var pathManager = Context.Instance.PathManager;
var campaign = Context.Instance.CampaignName;
var resType = LGS.ResourceType.Texture;
var textureNames = pathManager.GetResourceNames(resType, campaign);
foreach (var name in textureNames)
{
var (_, path) = pathManager.GetResourcePath(resType, campaign, name);
var texture = TextureLoader.LoadTexture(path);
var textureRect = new TextureRect();
textureRect.Texture = texture;
textureRect.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
textureRect.StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered;
textureRect.SetAnchorsPreset(Control.LayoutPreset.FullRect);
2024-09-08 17:29:58 +00:00
// We use meta here rather than just Name because Name replaces / with _ so
// we can't reliably construct it (the path can have natural _)
var slot = new Panel();
2024-09-08 17:29:58 +00:00
slot.SetMeta("TexPath", name);
slot.CustomMinimumSize = new Vector2(128, 128);
slot.AddChild(textureRect);
slot.GuiInput += (input) =>
{
if (input is InputEventMouseButton mouseEvent &&
mouseEvent.Pressed &&
mouseEvent.ButtonIndex == MouseButton.Left)
{
2024-09-08 18:24:21 +00:00
SetPreviewTexture(name, texture, path);
}
};
_textureList.AddChild(slot);
}
}
2024-09-08 18:24:21 +00:00
private void SetPreviewTexture(string name, Texture2D texture, string path)
{
_previewTexture.Texture = texture;
_texturePath.Text = name;
2024-09-08 18:24:21 +00:00
_fileType.Text = Path.GetExtension(path).ToUpper();
}
private void ApplySearchFilter(string filter)
{
2024-09-08 17:29:58 +00:00
var regex = $"^{_searchFilterPrefix}.*{Regex.Escape(filter).Replace("\\*", ".*")}.*$";
foreach (var node in _textureList.GetChildren())
{
var panel = (Panel)node;
2024-09-08 17:29:58 +00:00
var name = panel.GetMeta("TexPath").ToString();
panel.Visible = Regex.IsMatch(name, regex);
}
}
2024-09-08 16:22:47 +00:00
private void ApplySortMode(long id)
{
var popup = _sortMenu.GetPopup();
for (var i = 0; i < popup.ItemCount; i++)
{
popup.SetItemChecked(i, popup.GetItemId(i) == id);
}
// TODO: Actualy sort
}
2024-09-08 17:29:58 +00:00
private void SetActiveFolder()
{
var selected = _folderTree.GetSelected();
var selectedFolder = selected.GetText(0) + "/";
var parent = selected.GetParent();
while (parent != null)
{
var text = parent.GetText(0);
if (text != "")
{
selectedFolder = text + "/" + selectedFolder;
}
parent = parent.GetParent();
}
// This prefix is used in a regex, so we escape it here. The additional
// replace is because C# Regex doesn't escape /
_searchFilterPrefix = Regex.Escape(selectedFolder).Replace("/", "\\/");
ApplySearchFilter(_searchBar.Text);
}
2024-09-08 18:18:44 +00:00
private void SetPreviewTextureFilter(long idx)
{
var filterMode = idx switch
{
0 => CanvasItem.TextureFilterEnum.LinearWithMipmaps,
1 => CanvasItem.TextureFilterEnum.NearestWithMipmaps,
_ => throw new InvalidOperationException(),
};
_previewTexture.TextureFilter = filterMode;
}
}