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