wpf游戏引擎的contentBrowser实现

发布于:2025-06-12 ⋅ 阅读:(738) ⋅ 点赞:(0)

1.contentBrowser.cs

using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using PrimalEditor.Common;
using PrimalEditor.Utilities;
using PrimalEditor.GameProject;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Project = PrimalEditor.GameProject.Project;
using Microsoft.Win32;

namespace PrimalEditor.Content
{
    sealed class ContentInfo
    {

        public static int IconWidth => 90;
        public byte[] Icon { get; }

        public byte[] IconSmall { get; }

        public string FullPath { get;}

        public string FileName => Path.GetFileNameWithoutExtension(FullPath);

        public bool IsDirectory { get;  }

        public DateTime DateModified { get; }

        public long? Size { get; }

        public ContentInfo(string fullPath, byte[] icon = null, byte[] smallIcon = null, DateTime? lastModified = null) 
        {

            Debug.Assert(File.Exists(fullPath) || Directory.Exists(fullPath));
            var info = new FileInfo(fullPath);
            IsDirectory = ContentHelper.IsDirectory(fullPath);
            DateModified = lastModified ?? info.LastWriteTime;
            Size = IsDirectory ? (long?)null : info.Length;
            Icon = icon;
            IconSmall = IconSmall ?? icon;
            FullPath = fullPath;

        }


    }
    


    class ContentBrowser : ViewModelBase,IDisposable
    {
        private static readonly object _lock = new object();
        private static readonly DelayEventTimer _refreshTimer = new DelayEventTimer(TimeSpan.FromMicroseconds(250));
        private static readonly FileSystemWatcher _contentWatcher = new FileSystemWatcher()
        {
            IncludeSubdirectories = true,
            Filter = "",
            NotifyFilter = NotifyFilters.CreationTime |
                            NotifyFilters.DirectoryName |
                            NotifyFilters.FileName |
                            NotifyFilters.LastWrite

        };


        private string _cacheFilePath = string.Empty;
        private static readonly Dictionary<string, ContentInfo> _contentInfoCache = new Dictionary<string, ContentInfo>();

        public string ContentFolder { get; }


        private readonly ObservableCollection<ContentInfo> _folderContent = new ObservableCollection<ContentInfo>();

        public ReadOnlyObservableCollection<ContentInfo> FolderContent { get; }


        private string _selectedFolder;

        public string SelectedFolder 
        {
        
            get => _selectedFolder;
            set 
            {

                if (_selectedFolder != value) 
                {


                    _selectedFolder = value;

                    if (!string.IsNullOrEmpty(_selectedFolder)) 
                    {
                    
                        GetFolderContent();
                    
                    
                    }

                    OnPropertyChanged(nameof(SelectedFolder));
                
                
                }
            
            
            
            }
        
        
        
        
        }

        private async void GetFolderContent() 
        {
            var folderContent = new List<ContentInfo>();
            await Task.Run(() => {

                folderContent = GetFolderContent(SelectedFolder);
            
            });

            _folderContent.Clear();
            folderContent.ForEach(x => _folderContent.Add(x));


        
        }

        private static List<ContentInfo> GetFolderContent(string path) 
        {

            Debug.Assert(!string.IsNullOrEmpty(path));
            var folderContent = new List<ContentInfo>();


            try 
            {

                foreach (var dir in Directory.GetDirectories(path)) 
                {

                    folderContent.Add(new ContentInfo(dir));
                
                }

                //Get files
                lock (_lock)
                {

                    foreach (var file in Directory.GetFiles(path, $"*{Asset.AssetFileExtension}"))
                    {
                        var fileInfo = new FileInfo(file);

                        if (!_contentInfoCache.ContainsKey(file) ||
                            _contentInfoCache[file].DateModified.IsOlder(fileInfo.LastWriteTime))
                        {
                            var info = AssetRegistry.GetAssetInfo(file) ?? Asset.GetAssetInfo(file);
                            Debug.Assert(info != null);
                            _contentInfoCache[file] = new ContentInfo(file, info.Icon);

                        }

                        Debug.Assert(_contentInfoCache.ContainsKey(file));
                        folderContent.Add(_contentInfoCache[file]);

                    }

                }
            
            }catch (Exception ex) 
            {

                Debug.WriteLine(ex.Message);
            
            }

            return folderContent;
        
        
        }

        

        private async void OnContentModified(object sender, FileSystemEventArgs e)
        {
            if (Path.GetDirectoryName(e.FullPath) != SelectedFolder) return;

            await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                _refreshTimer.Trigger();

            }));


        }


        private void Refresh(object? sender, DelayEventTimerArgs e)
        {

            GetFolderContent();
           

        }


        private static void SaveInfoCache(string file) 
        {

            lock (_lock) 
            {
                using var writer = new BinaryWriter(File.Open(file, FileMode.Create, FileAccess.Write));
                writer.Write(_contentInfoCache.Keys.Count);
                foreach (var key in _contentInfoCache.Keys) 
                {
                
                    var info = _contentInfoCache[key];

                    writer.Write(key);
                    writer.Write(info.DateModified.ToBinary());
                    writer.Write(info.Icon.Length);
                    writer.Write(info.Icon);
                
                
                }
            
            }
        
        
        
        }


        private static void LoadInfoCache(string file) 
        {

            if (!File.Exists(file)) return;

            try 
            {

                lock (_lock)
                {
                    using var reader = new BinaryReader(File.Open(file, FileMode.Open, FileAccess.Read));
                    var numEntries = reader.ReadInt32();
                    _contentInfoCache.Clear();

                    for (int i = 0; i < numEntries; ++i) 
                    {
                        
                        var assetFile = reader.ReadString();
                        var date = DateTime.FromBinary(reader.ReadInt64());
                        var iconSize = reader.ReadInt32();
                        var icon = reader.ReadBytes(iconSize);


                        //Cache only the files that still exist
                        if (File.Exists(assetFile)) 
                        {

                            _contentInfoCache[assetFile] = new ContentInfo(assetFile, icon, null, date);
                        
                        
                        }

                    
                    
                    }
                
                
                }
                
            } catch (Exception ex) 
            {

                Debug.WriteLine(ex.Message);
                Logger.Log(MessageType.Warning, "Failed to read Content Browser cache file");
                _contentInfoCache.Clear();


            }
        
        
        
        }

        public void Dispose()
        {

            ((IDisposable)_contentWatcher).Dispose();
            if (!string.IsNullOrEmpty(_cacheFilePath))
            {

                SaveInfoCache(_cacheFilePath);
                _cacheFilePath = string.Empty;


            }

        }

        public ContentBrowser(Project project) 
        {
            

            Debug.Assert(project != null);
            var contentFolder = project.ContentPath;
            Debug.Assert(!string.IsNullOrEmpty(contentFolder.Trim()));
            contentFolder = Path.TrimEndingDirectorySeparator(contentFolder);
            ContentFolder = contentFolder;
            SelectedFolder = contentFolder;
            FolderContent = new ReadOnlyObservableCollection<ContentInfo>(_folderContent);

            if (string.IsNullOrEmpty(_cacheFilePath)) 
            {

                _cacheFilePath = $@"{project.Path}.Primal\ContentInfoCache.bin";
                LoadInfoCache(_cacheFilePath);
            
            }


        
            _contentWatcher.Path = contentFolder;
            _contentWatcher.Changed += OnContentModified;
            _contentWatcher.Created += OnContentModified;
            _contentWatcher.Deleted += OnContentModified;
            _contentWatcher.Renamed += OnContentModified;
            _contentWatcher.EnableRaisingEvents = true;


            _refreshTimer.Triggered += Refresh;

        }

     
    }

}
 


网站公告

今日签到

点亮在社区的每一天
去签到