init
This commit is contained in:
commit
d04ec57ba7
8
Editor.meta
Normal file
8
Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7811143a39f50c944bfc16e166522b84
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
18
Editor/AlicizaX.PackageManager.Editor.asmdef
Normal file
18
Editor/AlicizaX.PackageManager.Editor.asmdef
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "AlicizaX.PackageManager.Editor",
|
||||
"rootNamespace": "AlicizaX.PackageManager.Editor",
|
||||
"references": [
|
||||
"GUID:478a2357cc57436488a56e564b08d223"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
7
Editor/AlicizaX.PackageManager.Editor.asmdef.meta
Normal file
7
Editor/AlicizaX.PackageManager.Editor.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2530939212d0e04b9969f32af76be2c
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Editor/PackageManager.meta
Normal file
8
Editor/PackageManager.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32d329a1ac6add4408e5effdee7a429d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
144
Editor/PackageManager/PackageManagerCheckTool.cs
Normal file
144
Editor/PackageManager/PackageManagerCheckTool.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
public static class PackageManagerCheckTool
|
||||
{
|
||||
private static Dictionary<string, string> cachedManifestDependencies;
|
||||
private static ListRequest listRequest;
|
||||
|
||||
[Serializable]
|
||||
public class ManifestDependencies
|
||||
{
|
||||
public Dictionary<string, string> dependencies = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public static void ValidatePackageStates(List<RepositoryPackageData> packages)
|
||||
{
|
||||
var manifest = ReadManifestFile();
|
||||
var installedPackages = GetInstalledPackagesFromPM();
|
||||
|
||||
foreach (var package in packages)
|
||||
{
|
||||
// 检查是否在manifest中存在
|
||||
if (manifest.dependencies.TryGetValue(package.name, out var gitUrl))
|
||||
{
|
||||
// 检查PackageCache目录
|
||||
var cachePath = FindInPackageCache(package.name);
|
||||
if (!string.IsNullOrEmpty(cachePath))
|
||||
{
|
||||
var localTime = Directory.GetLastWriteTimeUtc(cachePath);
|
||||
if (DateTime.TryParse(package.updatedAt, out var remoteTime))
|
||||
{
|
||||
package.PackageState = remoteTime > localTime ? PackageState.Update : PackageState.Install;
|
||||
}
|
||||
else
|
||||
{
|
||||
package.PackageState = PackageState.Install;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
package.PackageState = PackageState.Install;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 检查Packages目录
|
||||
var packagePath = Path.GetFullPath($"Packages/{package.name}");
|
||||
package.PackageState = Directory.Exists(packagePath) ? PackageState.InstallLocal : PackageState.UnInstall;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindInPackageCache(string packageName)
|
||||
{
|
||||
var cacheRoot = Path.GetFullPath("Library/PackageCache");
|
||||
if (!Directory.Exists(cacheRoot)) return null;
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(cacheRoot))
|
||||
{
|
||||
if (dir.Contains(packageName + "@"))
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ManifestDependencies ReadManifestFile()
|
||||
{
|
||||
var manifestPath = Path.GetFullPath("Packages/manifest.json");
|
||||
var json = File.ReadAllText(manifestPath);
|
||||
return JsonConvert.DeserializeObject<ManifestDependencies>(json);
|
||||
}
|
||||
|
||||
public static void InstallPackage(string gitUrl)
|
||||
{
|
||||
Client.Add(gitUrl);
|
||||
}
|
||||
|
||||
public static void UpdatePackage(string packageName)
|
||||
{
|
||||
Client.Add($"git:{packageName}");
|
||||
}
|
||||
|
||||
public static void UninstallPackage(string packageName)
|
||||
{
|
||||
Client.Remove(packageName);
|
||||
}
|
||||
|
||||
private static List<string> GetInstalledPackagesFromPM()
|
||||
{
|
||||
var packages = new List<string>();
|
||||
listRequest = Client.List();
|
||||
|
||||
EditorApplication.CallbackFunction checkProgress = null;
|
||||
checkProgress = () =>
|
||||
{
|
||||
if (listRequest.IsCompleted)
|
||||
{
|
||||
if (listRequest.Status == StatusCode.Success)
|
||||
{
|
||||
foreach (var package in listRequest.Result)
|
||||
{
|
||||
packages.Add(package.name);
|
||||
}
|
||||
}
|
||||
|
||||
EditorApplication.update -= checkProgress;
|
||||
}
|
||||
};
|
||||
|
||||
EditorApplication.update += checkProgress;
|
||||
return packages;
|
||||
}
|
||||
|
||||
// 辅助方法:从Git URL提取包名
|
||||
public static string ExtractPackageNameFromUrl(string gitUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(gitUrl);
|
||||
string path = uri.AbsolutePath;
|
||||
int startIndex = path.LastIndexOf('/') + 1;
|
||||
string name = path.Substring(startIndex);
|
||||
return name.Replace(".git", "").Replace("_", ".").ToLower();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(gitUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/PackageManager/PackageManagerCheckTool.cs.meta
Normal file
3
Editor/PackageManager/PackageManagerCheckTool.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 491930ef63864219a4944a44ed820823
|
||||
timeCreated: 1740016880
|
204
Editor/PackageManager/RepositoryDataFetcher.cs
Normal file
204
Editor/PackageManager/RepositoryDataFetcher.cs
Normal file
@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using Newtonsoft.Json;
|
||||
using Unity.EditorCoroutines.Editor;
|
||||
using UnityEditor;
|
||||
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
public enum PackageState
|
||||
{
|
||||
UnInstall,
|
||||
InstallLocal,
|
||||
Install,
|
||||
Update
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RepositoryPackageData
|
||||
{
|
||||
// Repository info
|
||||
public string name;
|
||||
public string description;
|
||||
public string cloneUrl;
|
||||
public string htmlUrl;
|
||||
public string updatedAt;
|
||||
|
||||
// Package info
|
||||
public string displayName;
|
||||
public string version;
|
||||
public string unityVersion;
|
||||
public string category;
|
||||
public string packageDescription;
|
||||
public string authorName;
|
||||
public PackageState PackageState;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RepoApiResponse
|
||||
{
|
||||
public bool ok;
|
||||
public List<RepoItem> data;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RepoItem
|
||||
{
|
||||
public string name;
|
||||
public string description;
|
||||
public string clone_url;
|
||||
public string html_url;
|
||||
public string updated_at;
|
||||
public Owner owner;
|
||||
|
||||
[Serializable]
|
||||
public class Owner
|
||||
{
|
||||
public string login;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PackageJsonResponse
|
||||
{
|
||||
public string name;
|
||||
public string displayName;
|
||||
public string category;
|
||||
public string description;
|
||||
public string version;
|
||||
public string unity;
|
||||
public Author author;
|
||||
|
||||
[Serializable]
|
||||
public class Author
|
||||
{
|
||||
public string name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RepositoryDataFetcher
|
||||
{
|
||||
private const string BaseApiUrl = "http://101.34.252.46:3000/api/v1/repos/search?q=unity&topic=false";
|
||||
private const string PackageJsonPath = "/raw/branch/main/package.json";
|
||||
|
||||
|
||||
|
||||
|
||||
public static void FetchRepositoryData(Action<List<RepositoryPackageData>> callback)
|
||||
{
|
||||
EditorCoroutineUtility.StartCoroutineOwnerless(FetchDataRoutine(callback));
|
||||
}
|
||||
|
||||
private static IEnumerator FetchDataRoutine(Action<List<RepositoryPackageData>> callback)
|
||||
{
|
||||
// 第一阶段:获取仓库列表
|
||||
using (var request = CreateWebRequest(BaseApiUrl))
|
||||
{
|
||||
yield return request.SendWebRequest();
|
||||
|
||||
if (!HandleRequestError(request, "Repository request failed"))
|
||||
{
|
||||
callback?.Invoke(null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
var repoResponse = JsonConvert.DeserializeObject<RepoApiResponse>(request.downloadHandler.text);
|
||||
if (!repoResponse.ok || repoResponse.data == null)
|
||||
{
|
||||
Debug.LogError("Invalid repository response data");
|
||||
callback?.Invoke(null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
var results = new List<RepositoryPackageData>();
|
||||
var pendingRequests = new Queue<RepoItem>(repoResponse.data);
|
||||
|
||||
// 第二阶段:逐个获取package.json信息
|
||||
while (pendingRequests.Count > 0)
|
||||
{
|
||||
var repo = pendingRequests.Dequeue();
|
||||
var packageData = CreateBaseRepositoryData(repo);
|
||||
|
||||
var packageUrl = repo.html_url + PackageJsonPath;
|
||||
using (var packageRequest = CreateWebRequest(packageUrl))
|
||||
{
|
||||
yield return packageRequest.SendWebRequest();
|
||||
|
||||
if (HandleRequestError(packageRequest, $"Package request failed for {repo.name}"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var packageInfo = JsonConvert.DeserializeObject<PackageJsonResponse>(
|
||||
packageRequest.downloadHandler.text);
|
||||
|
||||
UpdateWithPackageInfo(ref packageData, packageInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"JSON parse error for {repo.name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(packageData);
|
||||
}
|
||||
|
||||
// 防止同时发起太多请求,每帧处理一个
|
||||
yield return null;
|
||||
}
|
||||
|
||||
callback?.Invoke(results);
|
||||
}
|
||||
}
|
||||
|
||||
private static UnityWebRequest CreateWebRequest(string url)
|
||||
{
|
||||
var request = new UnityWebRequest(url, "GET");
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
request.SetRequestHeader("accept", "application/json");
|
||||
return request;
|
||||
}
|
||||
|
||||
private static bool HandleRequestError(UnityWebRequest request, string errorMessage)
|
||||
{
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
Debug.LogError($"{errorMessage}: {request.error}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static RepositoryPackageData CreateBaseRepositoryData(RepoItem repo)
|
||||
{
|
||||
return new RepositoryPackageData
|
||||
{
|
||||
name = repo.name,
|
||||
description = repo.description,
|
||||
cloneUrl = repo.clone_url,
|
||||
htmlUrl = repo.html_url,
|
||||
updatedAt = repo.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
private static void UpdateWithPackageInfo(ref RepositoryPackageData data, PackageJsonResponse package)
|
||||
{
|
||||
if (package == null) return;
|
||||
|
||||
data.displayName = package.displayName;
|
||||
data.version = package.version;
|
||||
data.unityVersion = package.unity;
|
||||
data.category = package.category;
|
||||
data.packageDescription = package.description;
|
||||
|
||||
if (package.author != null)
|
||||
{
|
||||
data.authorName = package.author.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/PackageManager/RepositoryDataFetcher.cs.meta
Normal file
3
Editor/PackageManager/RepositoryDataFetcher.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 311855e386934def95705701938ac3f5
|
||||
timeCreated: 1739878350
|
8
Editor/PackageManager/Res.meta
Normal file
8
Editor/PackageManager/Res.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c43bb4a9eaf8b147bb5e9145abda9a1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,6 @@
|
||||
<engine:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:engine="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
|
||||
<engine:TwoPaneSplitView fixed-pane-index="0" fixed-pane-initial-dimension="250" tabindex="0">
|
||||
<engine:VisualElement name="LeftContainer" />
|
||||
<engine:VisualElement name="RightContainer" style="flex-grow: 1; border-left-width: 1px; border-left-color: rgb(0, 0, 0);" />
|
||||
</engine:TwoPaneSplitView>
|
||||
</engine:UXML>
|
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 432e048f6b943cf408f1ab3c0ef1310c
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
3
Editor/PackageManager/Window.meta
Normal file
3
Editor/PackageManager/Window.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f48ee37e4be54c94bd6a2fb80024d3d8
|
||||
timeCreated: 1739951411
|
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
[EditorWindowTitle(title = "Package Manager", icon = "Package Manager")]
|
||||
internal class FrameworkPackageManagerWindow : EditorWindow
|
||||
{
|
||||
private const string VisualAssetPath = "Packages/com.alicizax.unity.packagemanager/Editor/PackageManager/Res/AlicizaXPackageManagerWindow.uxml";
|
||||
public List<RepositoryPackageData> RepositoryPackageDatas = new List<RepositoryPackageData>();
|
||||
|
||||
|
||||
public void RefreshPackageData(Action callBack)
|
||||
{
|
||||
RepositoryDataFetcher.FetchRepositoryData((datas) =>
|
||||
{
|
||||
PackageManagerCheckTool.ValidatePackageStates(datas);
|
||||
RepositoryPackageDatas = datas;
|
||||
callBack?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
[MenuItem("Tools/AlicizaX/Package Manager")]
|
||||
internal static void Open()
|
||||
{
|
||||
EditorWindow.GetWindow<FrameworkPackageManagerWindow>().Show();
|
||||
GetWindow<FrameworkPackageManagerWindow>().Refresh();
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
VisualTreeAsset visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(VisualAssetPath);
|
||||
visualTree.CloneTree(rootVisualElement);
|
||||
CreateLeftList();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
RefreshPackageData(() =>
|
||||
{
|
||||
packageListView.itemsSource = RepositoryPackageDatas;
|
||||
packageListView.Rebuild();
|
||||
});
|
||||
}
|
||||
|
||||
private PackageListView packageListView;
|
||||
private PackageInfoView packageInfoView;
|
||||
|
||||
private void CreateLeftList()
|
||||
{
|
||||
var leftContainer = rootVisualElement.Q<VisualElement>("LeftContainer");
|
||||
leftContainer.style.backgroundColor = new Color(0.1686275f, 0.1606275f, 0.1686275f);
|
||||
|
||||
packageListView = new PackageListView();
|
||||
leftContainer.Add(packageListView);
|
||||
packageListView.OnSelectionChangedEvent += PackageListViewOnOnSelectionChangedEvent;
|
||||
|
||||
|
||||
leftContainer.Add(new PackageBottomStateBar());
|
||||
|
||||
var rightContainer = rootVisualElement.Q<VisualElement>("RightContainer");
|
||||
|
||||
packageInfoView = new PackageInfoView();
|
||||
rightContainer.Add(packageInfoView);
|
||||
|
||||
|
||||
packageListView.itemsSource = RepositoryPackageDatas;
|
||||
}
|
||||
|
||||
private void PackageListViewOnOnSelectionChangedEvent(IEnumerable<RepositoryPackageData> obj)
|
||||
{
|
||||
packageInfoView.RefreshView(obj.First());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29f884ddb4ba425fbb082406fe4da3cf
|
||||
timeCreated: 1739944505
|
44
Editor/PackageManager/Window/PackageBottomStateBar.cs
Normal file
44
Editor/PackageManager/Window/PackageBottomStateBar.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
internal class PackageBottomStateBar : VisualElement
|
||||
{
|
||||
private readonly Label stateLabel;
|
||||
private readonly Button btnRefresh;
|
||||
|
||||
public PackageBottomStateBar()
|
||||
{
|
||||
style.flexDirection = FlexDirection.Row;
|
||||
style.flexShrink = 1;
|
||||
style.justifyContent = Justify.FlexStart;
|
||||
style.height = Length.Percent(5);
|
||||
style.backgroundColor = new Color(0.2509804f, 0.2509804f, 0.2509804f);
|
||||
style.borderTopWidth = 1;
|
||||
|
||||
stateLabel = new Label();
|
||||
stateLabel.name = "stateLabel";
|
||||
stateLabel.text = "Last update Feb 19,13:52";
|
||||
stateLabel.style.alignSelf = Align.Center;
|
||||
stateLabel.style.marginLeft = new Length(2, LengthUnit.Pixel);
|
||||
stateLabel.style.flexGrow = 1; // 让 Label 占据剩余空间
|
||||
Add(stateLabel);
|
||||
|
||||
btnRefresh = new Button();
|
||||
btnRefresh.name = "btnRefresh";
|
||||
btnRefresh.style.maxHeight = 30;
|
||||
|
||||
Image icon = new Image();
|
||||
icon.image = EditorGUIUtility.IconContent("d_Refresh").image;
|
||||
btnRefresh.Add(icon);
|
||||
Add(btnRefresh);
|
||||
}
|
||||
|
||||
private void OnBtnRefreshClick()
|
||||
{
|
||||
// RepositoryDataFetcher.RefreshPackageData(null);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef4c9b64d076487cae6a6e58ab137fb4
|
||||
timeCreated: 1739950003
|
116
Editor/PackageManager/Window/PackageInfoListItem.cs
Normal file
116
Editor/PackageManager/Window/PackageInfoListItem.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
internal class PackageInfoListItem : VisualElement
|
||||
{
|
||||
private RepositoryPackageData _repositoryPackageData;
|
||||
|
||||
private VisualElement m_MainItem;
|
||||
private Label m_NameLabel;
|
||||
private Label m_VersionLabel;
|
||||
private VisualElement m_StateContainer;
|
||||
private Image m_StateIcon;
|
||||
private VisualElement m_LeftContainer;
|
||||
private VisualElement m_RightContainer;
|
||||
|
||||
public PackageInfoListItem()
|
||||
{
|
||||
style.backgroundColor = new Color(0.2196078f, 0.2196078f, 0.2196078f);
|
||||
BuildMainItem();
|
||||
}
|
||||
|
||||
public void RefreshDataSource(RepositoryPackageData packageData)
|
||||
{
|
||||
_repositoryPackageData = packageData;
|
||||
m_NameLabel.text = packageData.displayName;
|
||||
|
||||
if (packageData.PackageState == PackageState.Update)
|
||||
{
|
||||
m_StateIcon.image = EditorGUIUtility.IconContent("Update-Available").image;
|
||||
}
|
||||
else if (packageData.PackageState == PackageState.Install || packageData.PackageState == PackageState.InstallLocal)
|
||||
{
|
||||
m_StateIcon.image = EditorGUIUtility.IconContent("Installed").image;
|
||||
}
|
||||
else if (packageData.PackageState == PackageState.UnInstall)
|
||||
{
|
||||
m_StateIcon.image = EditorGUIUtility.IconContent("d_CreateAddNew").image;
|
||||
}
|
||||
|
||||
m_VersionLabel.text = packageData.PackageState == PackageState.InstallLocal ? "[Local]" : packageData.version;
|
||||
}
|
||||
|
||||
private void BuildMainItem()
|
||||
{
|
||||
style.flexShrink = 1;
|
||||
style.borderBottomWidth = 1;
|
||||
var borderColor = new Color(0.1372549f, 0.1372549f, 0.1372549f);
|
||||
style.borderBottomColor = borderColor;
|
||||
style.borderTopColor = borderColor;
|
||||
style.borderLeftColor = borderColor;
|
||||
style.borderRightColor = borderColor;
|
||||
|
||||
m_MainItem = new VisualElement();
|
||||
m_MainItem.name = "MainItem";
|
||||
m_MainItem.style.height = Length.Percent(100);
|
||||
m_MainItem.style.flexDirection = FlexDirection.Row;
|
||||
m_MainItem.style.alignItems = Align.Center;
|
||||
|
||||
Add(m_MainItem);
|
||||
|
||||
m_LeftContainer = new VisualElement();
|
||||
m_LeftContainer.name = "leftContainer";
|
||||
|
||||
m_MainItem.Add(m_LeftContainer);
|
||||
|
||||
m_NameLabel = new Label();
|
||||
m_NameLabel.name = "packageName";
|
||||
m_LeftContainer.Add((VisualElement)m_NameLabel);
|
||||
m_LeftContainer.style.marginLeft = new Length(20, LengthUnit.Pixel);
|
||||
m_LeftContainer.style.justifyContent = Justify.Center;
|
||||
m_LeftContainer.style.flexGrow = 1; // 让 Label 占据剩余空间
|
||||
|
||||
m_VersionLabel = new Label();
|
||||
m_VersionLabel.name = "versionLabel";
|
||||
m_MainItem.Add((VisualElement)m_VersionLabel);
|
||||
|
||||
m_RightContainer = new VisualElement()
|
||||
{
|
||||
name = "rightContainer",
|
||||
};
|
||||
m_RightContainer.AddToClassList("right");
|
||||
m_MainItem.Add(m_RightContainer);
|
||||
|
||||
|
||||
m_StateContainer = new VisualElement()
|
||||
{
|
||||
name = "statesContainer"
|
||||
};
|
||||
m_MainItem.Add(m_StateContainer);
|
||||
|
||||
m_StateContainer.style.justifyContent = Justify.Center;
|
||||
m_StateContainer.style.marginRight = new Length(10, LengthUnit.Pixel);
|
||||
|
||||
m_StateIcon = new Image()
|
||||
{
|
||||
name = "stateIcon",
|
||||
};
|
||||
m_StateIcon.AddToClassList("status");
|
||||
m_StateContainer.Add(m_StateIcon);
|
||||
}
|
||||
|
||||
public void SetSelected(bool isSelected)
|
||||
{
|
||||
if (m_MainItem != null)
|
||||
{
|
||||
// 设置选中时的背景颜色为 #4D4D4D(RGB: 77,77,77),未选中时透明
|
||||
m_MainItem.style.backgroundColor = isSelected
|
||||
? new Color(77f / 255f, 77f / 255f, 77f / 255f)
|
||||
: Color.clear;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/PackageManager/Window/PackageInfoListItem.cs.meta
Normal file
3
Editor/PackageManager/Window/PackageInfoListItem.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45ecac0747e241e188e33b298d93dbe9
|
||||
timeCreated: 1739947858
|
206
Editor/PackageManager/Window/PackageInfoView.cs
Normal file
206
Editor/PackageManager/Window/PackageInfoView.cs
Normal file
@ -0,0 +1,206 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
internal class PackageInfoView : VisualElement
|
||||
{
|
||||
private VisualElement _mainContainer;
|
||||
private VisualElement _headContainer;
|
||||
private Label _displayName;
|
||||
private Label _versionWithDate;
|
||||
private Label _authorLabel;
|
||||
private Label _packageNameLabel;
|
||||
private Label _projectDescription;
|
||||
private Label _btnLinkHtml;
|
||||
|
||||
|
||||
private VisualElement packageBtnContainer;
|
||||
|
||||
|
||||
private Button _btnUpdate;
|
||||
private Button _btnInstall;
|
||||
private Button _btnRemove;
|
||||
|
||||
|
||||
private RepositoryPackageData _selectRepositoryPackageData;
|
||||
|
||||
public PackageInfoView()
|
||||
{
|
||||
style.flexShrink = 1;
|
||||
style.height = Length.Percent(100);
|
||||
|
||||
_mainContainer = new VisualElement()
|
||||
{
|
||||
name = "MainContainer"
|
||||
};
|
||||
_mainContainer.style.flexShrink = 1;
|
||||
_mainContainer.style.height = Length.Percent(100);
|
||||
|
||||
|
||||
_headContainer = new VisualElement()
|
||||
{
|
||||
name = "HeadContainer"
|
||||
};
|
||||
_headContainer.style.flexShrink = 1;
|
||||
_headContainer.style.height = Length.Percent(35);
|
||||
_headContainer.style.paddingLeft = 10;
|
||||
_headContainer.style.paddingTop = 10;
|
||||
|
||||
_mainContainer.Add(_headContainer);
|
||||
|
||||
|
||||
_displayName = new Label()
|
||||
{
|
||||
name = "DisplayName"
|
||||
};
|
||||
_displayName.style.fontSize = 19;
|
||||
_displayName.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
|
||||
_versionWithDate = new Label()
|
||||
{
|
||||
name = "VersionWithDate"
|
||||
};
|
||||
_versionWithDate.style.fontSize = 13;
|
||||
_versionWithDate.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
_versionWithDate.style.paddingTop = 5;
|
||||
|
||||
|
||||
_authorLabel = new Label()
|
||||
{
|
||||
name = "AuthorLabel"
|
||||
};
|
||||
_authorLabel.style.fontSize = 12;
|
||||
_authorLabel.style.paddingTop = 5;
|
||||
_authorLabel.style.unityFontStyleAndWeight = FontStyle.Italic;
|
||||
|
||||
_packageNameLabel = new Label()
|
||||
{
|
||||
name = "PackageName"
|
||||
};
|
||||
_packageNameLabel.style.fontSize = 12;
|
||||
_packageNameLabel.style.paddingTop = 5;
|
||||
_packageNameLabel.style.unityFontStyleAndWeight = FontStyle.Italic;
|
||||
|
||||
_projectDescription = new Label()
|
||||
{
|
||||
name = "ProjectDescription"
|
||||
};
|
||||
_projectDescription.style.fontSize = 12;
|
||||
_projectDescription.style.paddingTop = 5;
|
||||
|
||||
|
||||
_btnLinkHtml = new Label()
|
||||
{
|
||||
name = "BtnLink"
|
||||
};
|
||||
|
||||
_btnLinkHtml.text = "Source Code";
|
||||
_btnLinkHtml.style.fontSize = 12;
|
||||
_btnLinkHtml.style.paddingTop = 5;
|
||||
_btnLinkHtml.style.marginLeft = 0;
|
||||
_btnLinkHtml.style.paddingLeft = 0;
|
||||
_btnLinkHtml.style.flexGrow = 0;
|
||||
_btnLinkHtml.style.minWidth = new Length(20, LengthUnit.Pixel);
|
||||
_btnLinkHtml.style.maxWidth = new Length(300, LengthUnit.Pixel);
|
||||
_btnLinkHtml.style.color = new Color(0.2980392f, 0.498f, 1f);
|
||||
_btnLinkHtml.style.backgroundColor = Color.clear;
|
||||
_btnLinkHtml.RegisterCallback<ClickEvent>(OnBtnLinkClick);
|
||||
|
||||
packageBtnContainer = new VisualElement()
|
||||
{
|
||||
name = "PackageBtnContainer"
|
||||
};
|
||||
|
||||
packageBtnContainer.style.flexShrink = 1;
|
||||
packageBtnContainer.style.height = Length.Percent(100);
|
||||
packageBtnContainer.style.flexDirection = FlexDirection.Row;
|
||||
packageBtnContainer.style.paddingTop = 10;
|
||||
|
||||
|
||||
_headContainer.Add(_displayName);
|
||||
_headContainer.Add(_versionWithDate);
|
||||
_headContainer.Add(_authorLabel);
|
||||
_headContainer.Add(_packageNameLabel);
|
||||
_headContainer.Add(_projectDescription);
|
||||
_headContainer.Add(_btnLinkHtml);
|
||||
_headContainer.Add(packageBtnContainer);
|
||||
|
||||
_btnUpdate = new Button(OnBtnUpdate)
|
||||
{
|
||||
name = "BtnUpdate"
|
||||
};
|
||||
_btnUpdate.text = "Update";
|
||||
_btnUpdate.style.width = new Length(90, LengthUnit.Pixel);
|
||||
_btnUpdate.style.height = new Length(24, LengthUnit.Pixel);
|
||||
_btnUpdate.style.marginLeft = 0;
|
||||
|
||||
_btnInstall = new Button(OnBtnInstall)
|
||||
{
|
||||
name = "BtnInstall"
|
||||
};
|
||||
_btnInstall.text = "Install";
|
||||
_btnInstall.style.width = new Length(90, LengthUnit.Pixel);
|
||||
_btnInstall.style.height = new Length(24, LengthUnit.Pixel);
|
||||
_btnInstall.style.marginLeft = 0;
|
||||
|
||||
_btnRemove = new Button(OnBtnRemove)
|
||||
{
|
||||
name = "BtnRemove"
|
||||
};
|
||||
_btnRemove.text = "Remove";
|
||||
_btnRemove.style.width = new Length(90, LengthUnit.Pixel);
|
||||
_btnRemove.style.height = new Length(24, LengthUnit.Pixel);
|
||||
_btnRemove.style.marginLeft = 0;
|
||||
|
||||
|
||||
|
||||
packageBtnContainer.Add(_btnInstall);
|
||||
packageBtnContainer.Add(_btnUpdate);
|
||||
packageBtnContainer.Add(_btnRemove);
|
||||
|
||||
Add(_mainContainer);
|
||||
RefreshView(_selectRepositoryPackageData);
|
||||
}
|
||||
|
||||
public void RefreshView(RepositoryPackageData repositoryPackageData)
|
||||
{
|
||||
if (repositoryPackageData == null)
|
||||
{
|
||||
_mainContainer.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_selectRepositoryPackageData = repositoryPackageData;
|
||||
|
||||
_mainContainer.visible = true;
|
||||
_displayName.text = repositoryPackageData.displayName;
|
||||
_versionWithDate.text = $"{repositoryPackageData.version}· {repositoryPackageData.updatedAt}";
|
||||
_authorLabel.text = $"Author: {repositoryPackageData.authorName}";
|
||||
_packageNameLabel.text = repositoryPackageData.name;
|
||||
_projectDescription.text = string.IsNullOrEmpty(repositoryPackageData.description) ? "Description:TODO" : repositoryPackageData.description;
|
||||
|
||||
|
||||
_btnInstall.style.display = repositoryPackageData.PackageState == PackageState.UnInstall ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_btnUpdate.style.display = repositoryPackageData.PackageState == PackageState.Update ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_btnRemove.style.display = repositoryPackageData.PackageState == PackageState.Update || repositoryPackageData.PackageState == PackageState.Install ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void OnBtnLinkClick(ClickEvent e)
|
||||
{
|
||||
Application.OpenURL(_selectRepositoryPackageData.htmlUrl);
|
||||
}
|
||||
|
||||
private void OnBtnUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnBtnInstall()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnBtnRemove()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/PackageManager/Window/PackageInfoView.cs.meta
Normal file
3
Editor/PackageManager/Window/PackageInfoView.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76b5dc77258547288eeaed68ca86a596
|
||||
timeCreated: 1739957408
|
69
Editor/PackageManager/Window/PackageListView.cs
Normal file
69
Editor/PackageManager/Window/PackageListView.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AlicizaX.PackageManager.Editor
|
||||
{
|
||||
internal class PackageListView : ListView
|
||||
{
|
||||
public event Action<IEnumerable<RepositoryPackageData>> OnSelectionChangedEvent;
|
||||
|
||||
private void OnSelectionChangedInternal(IEnumerable<object> selectedItems)
|
||||
{
|
||||
// 转换对象类型为实际数据类型
|
||||
var packages = selectedItems.Cast<RepositoryPackageData>();
|
||||
OnSelectionChangedEvent?.Invoke(packages);
|
||||
|
||||
// 刷新列表项显示
|
||||
RefreshItems();
|
||||
}
|
||||
|
||||
public PackageListView()
|
||||
{
|
||||
style.paddingTop = new Length(2, LengthUnit.Pixel);
|
||||
virtualizationMethod = CollectionVirtualizationMethod.FixedHeight;
|
||||
fixedItemHeight = 25f;
|
||||
horizontalScrollingEnabled = false;
|
||||
ScrollView scrollView = this.Q<ScrollView>();
|
||||
scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
|
||||
style.flexShrink = 1;
|
||||
style.height = Length.Percent(95);
|
||||
makeItem = MakeItem;
|
||||
bindItem = BindItem;
|
||||
selectionChanged += OnSelectionChanged;
|
||||
selectionChanged += OnSelectionChangedInternal;
|
||||
selectionType = SelectionType.Single;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(IEnumerable<object> selectedItems)
|
||||
{
|
||||
RefreshItems();
|
||||
}
|
||||
|
||||
private VisualElement MakeItem()
|
||||
{
|
||||
return new PackageInfoListItem();
|
||||
}
|
||||
|
||||
private void BindItem(VisualElement item, int index)
|
||||
{
|
||||
if (!(item is PackageInfoListItem packageItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RepositoryPackageData packageData = GetVisualStateByIndex(index);
|
||||
packageItem.RefreshDataSource(packageData);
|
||||
|
||||
// 绑定数据后更新选中状态
|
||||
bool isSelected = selectedIndices.Contains(index);
|
||||
packageItem.SetSelected(isSelected);
|
||||
}
|
||||
|
||||
private RepositoryPackageData GetVisualStateByIndex(int index)
|
||||
{
|
||||
return this.itemsSource is List<RepositoryPackageData> itemsSource ? itemsSource.ElementAtOrDefault<RepositoryPackageData>(index) : (RepositoryPackageData)null;
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/PackageManager/Window/PackageListView.cs.meta
Normal file
3
Editor/PackageManager/Window/PackageListView.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81fd4dd929c8461db6c870bef1127deb
|
||||
timeCreated: 1739952274
|
13
package.json
Normal file
13
package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "com.alicizax.unity.packagemanager",
|
||||
"displayName": "Aliciza X PackageManager",
|
||||
"version": "1.0.0",
|
||||
"description": "A Unity Framework",
|
||||
"author": {
|
||||
"name": "Yuliuren",
|
||||
"email": "yuliuren00@gmail.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.unity.editorcoroutines": "1.0.0"
|
||||
}
|
||||
}
|
7
package.json.meta
Normal file
7
package.json.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aaa00a70a6fc2044bd0b9145d855ed7
|
||||
PackageManifestImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user