mirror of
https://gitee.com/bimuziyan/ccc-skeleton-viewer.git
synced 2026-05-06 23:33:08 +08:00
适配 3.x
This commit is contained in:
@@ -94,7 +94,7 @@ Click on *Extension -> Skeleton Viewer -> View* option to open the view panel.
|
||||
|
||||
Click on *Extension -> Skeleton Viewer -> Settings* option to open the setting panel.
|
||||
|
||||
In the setting panel, you can choose a hotkey(shortcut, for opening the view panel quickly) in preset list, or customize one by yourself.
|
||||
In the setting panel, you can choose a shortcut key(for opening the view panel quickly) in preset list, or customize one by yourself.
|
||||
|
||||
One thing you should know, not every keys/keys-combinations can be used, because some keys/keys-combinations have been used by the system or Cocos Creator.
|
||||
|
||||
|
||||
16
i18n/en.js
16
i18n/en.js
@@ -40,17 +40,19 @@ module.exports = {
|
||||
'canvasColor': 'Canvas Color',
|
||||
// settings
|
||||
'none': 'None',
|
||||
'selectKey': 'Hotkey',
|
||||
'selectKeyTooltip': 'Choose a hotkey',
|
||||
'selectKey': 'Shortcut Key',
|
||||
'selectKeyTooltip': 'Choose a shortcut key',
|
||||
'customKey': 'Custom',
|
||||
'customKeyPlaceholder': 'Choose a hotkey above or customize one by yourself',
|
||||
'customKeyTooltip': 'You can also customize your own hotkey',
|
||||
'autoCheck': 'Auto Check Update',
|
||||
'autoCheckTooltip': 'Check if there is a new version when the extension is loaded',
|
||||
'customKeyPlaceholder': 'Choose a shortcut key above or customize one by yourself',
|
||||
'customKeyTooltip': 'You can also customize your own shortcut key',
|
||||
'alwaysOnTop': 'Always On Top',
|
||||
'alwaysOnTopTooltip': 'Keep view panel on top (3.3+)',
|
||||
'autoCheckUpdate': 'Auto Check Update',
|
||||
'autoCheckUpdateTooltip': 'Check if there is a new version when the extension is loaded',
|
||||
'reference': '· Hotkey customization reference: ',
|
||||
'accelerator': 'Keyboard Shortcuts',
|
||||
'repository': '· Git repository of this extension: ',
|
||||
'apply': 'Apply',
|
||||
'quoteError': 'Do not use double quotes!',
|
||||
'customKeyError': 'Please specify a hotkey!',
|
||||
'customKeyError': 'Please specify a shortcut key!',
|
||||
};
|
||||
|
||||
@@ -45,8 +45,10 @@ module.exports = {
|
||||
'customKey': '自定义',
|
||||
'customKeyPlaceholder': '在上方选择一个快捷键或自定义一个快捷键',
|
||||
'customKeyTooltip': '自定义快捷键',
|
||||
'autoCheck': '自动检查更新',
|
||||
'autoCheckTooltip': '扩展启动时自动检查是否有新版本',
|
||||
'alwaysOnTop': '窗口置顶',
|
||||
'alwaysOnTopTooltip': '预览窗口将始终显示在最前端 (3.3+)',
|
||||
'autoCheckUpdate': '自动检查更新',
|
||||
'autoCheckUpdateTooltip': '扩展启动时自动检查是否有新版本',
|
||||
'reference': '· 快捷键自定义请参考:',
|
||||
'accelerator': '键盘快捷键',
|
||||
'repository': '· 本扩展的 Git 仓库:',
|
||||
|
||||
BIN
images/icon.png
Normal file
BIN
images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 551 B |
138
package.json
138
package.json
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "ccc-skeleton-viewer",
|
||||
"version": "1.3.0.20210915",
|
||||
"package_version": 2,
|
||||
"version": "1.4.0.20220128",
|
||||
"description": "用于快速预览骨骼动画,提供独立窗口,也可以附着在编辑器中...",
|
||||
"author": {
|
||||
"name": "陈皮皮 (ifaswind)",
|
||||
"author": "陈皮皮 (ifaswind)",
|
||||
"author-info": {
|
||||
"email": "952157129@qq.com",
|
||||
"url": "https://chenpipi.cn",
|
||||
"wechat": "im_chenpipi",
|
||||
@@ -13,43 +14,104 @@
|
||||
"repository": "https://gitee.com/ifaswind/ccc-skeleton-viewer",
|
||||
"license": "MIT",
|
||||
"main": "src/main/index.js",
|
||||
"scene-script": "src/main/scene-worker.js",
|
||||
"main-menu": {
|
||||
"i18n:MAIN_MENU.package.title/i18n:ccc-skeleton-viewer.name/i18n:ccc-skeleton-viewer.view": {
|
||||
"message": "ccc-skeleton-viewer:open-view-panel",
|
||||
"icon": "/images/view.png"
|
||||
"contributions": {
|
||||
"menu": [
|
||||
{
|
||||
"path": "i18n:menu.extension/i18n:ccc-skeleton-viewer.name",
|
||||
"label": "i18n:ccc-skeleton-viewer.view",
|
||||
"message": "open-view-panel",
|
||||
"icon": "/images/view.png"
|
||||
},
|
||||
{
|
||||
"path": "i18n:menu.extension/i18n:ccc-skeleton-viewer.name",
|
||||
"label": "i18n:ccc-skeleton-viewer.settings",
|
||||
"message": "open-settings-panel",
|
||||
"icon": "/images/settings.png"
|
||||
},
|
||||
{
|
||||
"path": "i18n:menu.extension/i18n:ccc-skeleton-viewer.name",
|
||||
"label": "i18n:ccc-skeleton-viewer.checkUpdate",
|
||||
"message": "menu-check-update",
|
||||
"icon": "/images/update.png"
|
||||
},
|
||||
{
|
||||
"path": "i18n:menu.extension/i18n:ccc-skeleton-viewer.name",
|
||||
"label": "v1.3.0.20210915",
|
||||
"message": "menu-version",
|
||||
"icon": "/images/version.png"
|
||||
}
|
||||
],
|
||||
"messages": {
|
||||
"open-view-panel": {
|
||||
"public": true,
|
||||
"description": "打开预览面板",
|
||||
"methods": [
|
||||
"openViewPanel"
|
||||
]
|
||||
},
|
||||
"open-settings-panel": {
|
||||
"public": true,
|
||||
"description": "打开设置面板",
|
||||
"methods": [
|
||||
"openSettingsPanel"
|
||||
]
|
||||
},
|
||||
"menu-check-update": {
|
||||
"public": true,
|
||||
"description": "检查扩展是否有更新",
|
||||
"methods": [
|
||||
"menuCheckUpdate"
|
||||
]
|
||||
},
|
||||
"menu-version": {
|
||||
"public": false,
|
||||
"methods": [
|
||||
"menuVersion"
|
||||
]
|
||||
},
|
||||
"scene:ready": {
|
||||
"methods": [
|
||||
"onSceneReady"
|
||||
]
|
||||
},
|
||||
"selection:select": {
|
||||
"methods": [
|
||||
"onSelectionSelect"
|
||||
]
|
||||
}
|
||||
},
|
||||
"i18n:MAIN_MENU.package.title/i18n:ccc-skeleton-viewer.name/i18n:ccc-skeleton-viewer.settings": {
|
||||
"message": "ccc-skeleton-viewer:open-settings-panel",
|
||||
"icon": "/images/setting.png"
|
||||
},
|
||||
"i18n:MAIN_MENU.package.title/i18n:ccc-skeleton-viewer.name/i18n:ccc-skeleton-viewer.checkUpdate": {
|
||||
"message": "ccc-skeleton-viewer:menu-check-update",
|
||||
"icon": "/images/update.png"
|
||||
},
|
||||
"i18n:MAIN_MENU.package.title/i18n:ccc-skeleton-viewer.name/v1.3.0.20210915": {
|
||||
"message": "ccc-skeleton-viewer:menu-version",
|
||||
"icon": "/images/version.png"
|
||||
"shortcuts": [
|
||||
{
|
||||
"message": "open-view-panel",
|
||||
"mac": "",
|
||||
"win": ""
|
||||
}
|
||||
],
|
||||
"assets": {
|
||||
"menu": {
|
||||
"methods": "src/editor/assets-menu.js",
|
||||
"createMenu": "",
|
||||
"assetMenu": "onAssetMenu",
|
||||
"dbMenu": "",
|
||||
"panelMenu": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"panel.view": {
|
||||
"main": "src/renderer/view/entry.js",
|
||||
"type": "dockable",
|
||||
"title": "i18n:ccc-skeleton-viewer.name",
|
||||
"width": 500,
|
||||
"height": 600,
|
||||
"min-width": 300,
|
||||
"min-height": 250
|
||||
},
|
||||
"reload": {
|
||||
"renderer": [
|
||||
"src/renderer/**/*"
|
||||
],
|
||||
"ignore": [
|
||||
"config.json",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README.en.md"
|
||||
]
|
||||
"panels": {
|
||||
"view": {
|
||||
"title": "i18n:ccc-skeleton-viewer.name",
|
||||
"type": "dockable",
|
||||
"main": "src/renderer/view/entry.js",
|
||||
"icon": "/images/icon.png",
|
||||
"size": {
|
||||
"width": 500,
|
||||
"height": 600,
|
||||
"min-width": 300,
|
||||
"min-height": 250
|
||||
},
|
||||
"flags": {
|
||||
"alwaysOnTop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
const Path = require('path');
|
||||
const Fs = require('fs');
|
||||
const PackageUtil = require('../eazax/package-util');
|
||||
|
||||
/** 配置文件路径 */
|
||||
const CONFIG_PATH = Path.join(__dirname, '../../config.json');
|
||||
@@ -8,14 +7,8 @@ const CONFIG_PATH = Path.join(__dirname, '../../config.json');
|
||||
/** package.json 的路径 */
|
||||
const PACKAGE_PATH = Path.join(__dirname, '../../package.json');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/** 快捷键行为 */
|
||||
const ACTION_NAME = 'view';
|
||||
|
||||
/** package.json 中的菜单项 key */
|
||||
const MENU_ITEM_KEY = `i18n:MAIN_MENU.package.title/i18n:${PACKAGE_NAME}.name/i18n:${PACKAGE_NAME}.${ACTION_NAME}`;
|
||||
/** 快捷键消息 */
|
||||
const SHORTCUT_MESSAGE = 'open-view-panel';
|
||||
|
||||
/**
|
||||
* 配置管理器
|
||||
@@ -36,8 +29,8 @@ const ConfigManager = {
|
||||
* 读取配置
|
||||
*/
|
||||
get() {
|
||||
// 配置
|
||||
const config = ConfigManager.defaultConfig;
|
||||
// 配置
|
||||
if (Fs.existsSync(CONFIG_PATH)) {
|
||||
const localConfig = JSON.parse(Fs.readFileSync(CONFIG_PATH));
|
||||
for (const key in config) {
|
||||
@@ -46,54 +39,82 @@ const ConfigManager = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 快捷键
|
||||
config.hotkey = ConfigManager.getAccelerator();
|
||||
|
||||
// 快捷键和置顶
|
||||
const packageConfig = ConfigManager.getPackageConfig();
|
||||
config.shortcutKey = packageConfig.shortcutKey;
|
||||
config.alwaysOnTop = packageConfig.alwaysOnTop;
|
||||
// Done
|
||||
return config;
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
* @param {*} config 配置
|
||||
* @param {*} value 配置
|
||||
*/
|
||||
set(value) {
|
||||
// 配置
|
||||
const config = ConfigManager.defaultConfig;
|
||||
// 配置
|
||||
for (const key in config) {
|
||||
if (value[key] !== undefined) {
|
||||
config[key] = value[key];
|
||||
}
|
||||
}
|
||||
Fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
|
||||
// 快捷键
|
||||
ConfigManager.setAccelerator(value.hotkey);
|
||||
ConfigManager.setPackageConfig({
|
||||
shortcutKey: value.shortcutKey,
|
||||
alwaysOnTop: value.alwaysOnTop,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取快捷键
|
||||
* @returns {string}
|
||||
* 获取 package 配置
|
||||
* @returns {{ shortcutKey: string, alwaysOnTop: boolean }}
|
||||
*/
|
||||
getAccelerator() {
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH)),
|
||||
item = package['main-menu'][MENU_ITEM_KEY];
|
||||
return item['accelerator'] || '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置快捷键
|
||||
* @param {string} value
|
||||
*/
|
||||
setAccelerator(value) {
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH)),
|
||||
item = package['main-menu'][MENU_ITEM_KEY];
|
||||
if (value != undefined && value !== '') {
|
||||
item['accelerator'] = value;
|
||||
} else {
|
||||
delete item['accelerator'];
|
||||
getPackageConfig() {
|
||||
const config = {
|
||||
shortcutKey: '',
|
||||
alwaysOnTop: false,
|
||||
};
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH));
|
||||
// 快捷键
|
||||
const shortcuts = package['contributions']['shortcuts'];
|
||||
if (shortcuts && shortcuts.length > 0) {
|
||||
config.shortcutKey = shortcuts[0]['win'] || shortcuts[0]['mac'] || '';
|
||||
}
|
||||
// 置顶
|
||||
config.alwaysOnTop = package['panels']['view']['flags']['alwaysOnTop'];
|
||||
// Done
|
||||
return config;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置 package 配置
|
||||
* @param {{ shortcutKey: string, alwaysOnTop: boolean }} config
|
||||
*/
|
||||
setPackageConfig(config) {
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH));
|
||||
// 快捷键
|
||||
let shortcuts = package['contributions']['shortcuts'];
|
||||
if (!shortcuts) {
|
||||
shortcuts = package['contributions']['shortcuts'] = [];
|
||||
}
|
||||
let item = shortcuts[0];
|
||||
if (!item) {
|
||||
item = shortcuts[0] = {
|
||||
message: SHORTCUT_MESSAGE,
|
||||
mac: '',
|
||||
mac: '',
|
||||
};
|
||||
}
|
||||
if (config.shortcutKey != undefined) {
|
||||
item['win'] = item['mac'] = config.shortcutKey;
|
||||
} else {
|
||||
item['win'] = item['mac'] = '';
|
||||
}
|
||||
// 置顶
|
||||
package['panels']['view']['flags']['alwaysOnTop'] = config.alwaysOnTop;
|
||||
// 写入
|
||||
Fs.writeFileSync(PACKAGE_PATH, JSON.stringify(package, null, 2));
|
||||
},
|
||||
|
||||
|
||||
125
src/common/editor-adapter.js
Normal file
125
src/common/editor-adapter.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 编辑器适配器
|
||||
*/
|
||||
const EditorAdapter = {
|
||||
|
||||
/**
|
||||
* 获取编辑器语言
|
||||
* @returns {string}
|
||||
*/
|
||||
getLanguage() {
|
||||
return Editor.I18n.getLanguage();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源信息
|
||||
* @param {string} uuid
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getAssetInfoByUuid(uuid) {
|
||||
return Editor.Message.request('asset-db', 'query-asset-info', uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源 META
|
||||
* @param {string} uuid
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
getAssetMetaByUuid(uuid) {
|
||||
return Editor.Message.request('asset-db', 'query-asset-meta', uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源绝对路径
|
||||
* @param {string} uuid
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
getPathByUuid(uuid) {
|
||||
return Editor.Message.request('asset-db', 'query-path', uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源绝对路径
|
||||
* @param {string} url
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
getPathByUrl(url) {
|
||||
return Editor.Message.request('asset-db', 'query-path', url);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取资源 uuid
|
||||
* @param {string} path
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
getUuidByPath(path) {
|
||||
return Editor.Message.request('asset-db', 'query-uuid', path);
|
||||
},
|
||||
|
||||
/**
|
||||
* 面板
|
||||
*/
|
||||
Panel: {
|
||||
|
||||
/**
|
||||
* 打开面板
|
||||
* @param {string} panel
|
||||
*/
|
||||
open(panel) {
|
||||
Editor.Panel.open(panel);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭面板
|
||||
* @param {string} panel
|
||||
*/
|
||||
close(panel) {
|
||||
Editor.Panel.close(panel);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择
|
||||
*/
|
||||
Selection: {
|
||||
|
||||
/**
|
||||
* 清除编辑器选中
|
||||
* @param {'asset' | 'node'} type
|
||||
*/
|
||||
clear(type) {
|
||||
Editor.Selection.clear(type);
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除编辑器选中
|
||||
* @param {'asset' | 'node'} type
|
||||
* @param {string} uuid
|
||||
*/
|
||||
select(type, uuid) {
|
||||
Editor.Selection.select(type, uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取编辑器选中的类型
|
||||
* @returns {'asset' | 'node'}
|
||||
*/
|
||||
getSelectedType() {
|
||||
return Editor.Selection.getLastSelectedType();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取编辑器选中
|
||||
* @param {'asset' | 'node'} type
|
||||
* @returns {string}
|
||||
*/
|
||||
getSelected(type) {
|
||||
return Editor.Selection.getSelected(type);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EditorAdapter;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Cocos Creator 风格样式
|
||||
Cocos Creator (2.x) 编辑器风格样式
|
||||
版本: 20210911
|
||||
作者: 陈皮皮 (ifaswind)
|
||||
主页: https://gitee.com/ifaswind
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Cocos Creator 风格标签 (橙黑)
|
||||
Cocos Creator (2.x) 编辑器风格标签 (橙黑)
|
||||
版本: 20210725
|
||||
作者: 陈皮皮 (ifaswind)
|
||||
主页: https://gitee.com/ifaswind
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
Cocos Creator (2.x) 编辑器风格颜色 (橙黑)
|
||||
版本: 20210725
|
||||
作者: 陈皮皮 (ifaswind)
|
||||
主页: https://gitee.com/ifaswind
|
||||
公众号: 菜鸟小栈
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* 背景颜色 */
|
||||
--eazax-bg-color: #454545;
|
||||
|
||||
36
src/editor/assets-menu.js
Normal file
36
src/editor/assets-menu.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const RendererEvent = require("../eazax/renderer-event");
|
||||
|
||||
// 资源管理器菜单
|
||||
exports.onAssetMenu = function (assetInfo) {
|
||||
if (test(assetInfo.file)) {
|
||||
return [
|
||||
// 骨骼查看器 -> 预览
|
||||
{
|
||||
label: 'i18n:ccc-skeleton-viewer.name',
|
||||
submenu: [
|
||||
{
|
||||
label: 'i18n:ccc-skeleton-viewer.view',
|
||||
enabled: true,
|
||||
click() {
|
||||
// (主进程)预览
|
||||
RendererEvent.send('view', assetInfo.uuid);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 测试
|
||||
* @param {string} name
|
||||
*/
|
||||
function test(name) {
|
||||
return (
|
||||
name.endsWith('json') || name.endsWith('skel') ||
|
||||
name.endsWith('png') ||
|
||||
name.endsWith('atlas') || name.endsWith('txt')
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const MainEvent = require('../eazax/main-event');
|
||||
const EditorMainKit = require('../eazax/editor-main-kit');
|
||||
const { checkUpdate } = require('../eazax/editor-main-util');
|
||||
const { checkUpdate, reload } = require('../eazax/editor-main-util');
|
||||
const { openRepository } = require('../eazax/package-util');
|
||||
const ConfigManager = require('../common/config-manager');
|
||||
const Opener = require('./opener');
|
||||
const PanelManager = require('./panel-manager');
|
||||
const Updater = require('../eazax/updater');
|
||||
const EditorAdapter = require('../common/editor-adapter');
|
||||
|
||||
/**
|
||||
* 生命周期:加载
|
||||
*/
|
||||
function load() {
|
||||
// 设置仓库分支
|
||||
Updater.branch = 'v3';
|
||||
// 监听事件
|
||||
EditorMainKit.register();
|
||||
MainEvent.on('ready', onReadyEvent);
|
||||
MainEvent.on('close', onCloseEvent);
|
||||
MainEvent.on('reload', onReloadEvent);
|
||||
MainEvent.on('select', onSelectEvent);
|
||||
MainEvent.on('view', onViewEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,18 +32,49 @@ function unload() {
|
||||
// 取消事件监听
|
||||
EditorMainKit.unregister();
|
||||
MainEvent.removeAllListeners('ready');
|
||||
MainEvent.removeAllListeners('close');
|
||||
MainEvent.removeAllListeners('reload');
|
||||
MainEvent.removeAllListeners('select');
|
||||
MainEvent.removeAllListeners('view');
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)就绪事件回调
|
||||
* (渲染进程)预览面板就绪事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
*/
|
||||
function onReadyEvent(event) {
|
||||
// 保存预览面板的 WebContents
|
||||
PanelManager.viewWebContents = event.sender;
|
||||
// 检查编辑器选中
|
||||
Opener.checkEditorCurSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)预览面板关闭事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
*/
|
||||
function onCloseEvent(event) {
|
||||
PanelManager.viewWebContents = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)重新加载事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
*/
|
||||
function onReloadEvent(event) {
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)预览事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
*/
|
||||
function onViewEvent(event, uuid) {
|
||||
PanelManager.openViewPanel();
|
||||
EditorAdapter.Selection.clear('asset');
|
||||
EditorAdapter.Selection.select('asset', uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)选择文件事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
@@ -48,74 +88,65 @@ function onSelectEvent(event) {
|
||||
* @param {string} type 类型
|
||||
* @param {string[]} uuids uuids
|
||||
*/
|
||||
function onEditorSelection(type, uuids) {
|
||||
if (PanelManager.getViewPanel()) {
|
||||
function onSelectionSelect(type, uuids) {
|
||||
if (PanelManager.getViewPanelWebContents()) {
|
||||
Opener.identifySelection(type, uuids);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exports.load = load;
|
||||
|
||||
exports.unload = unload;
|
||||
|
||||
exports.methods = {
|
||||
|
||||
/**
|
||||
* 扩展消息
|
||||
* 打开预览面板
|
||||
*/
|
||||
messages: {
|
||||
|
||||
/**
|
||||
* 编辑器选中事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
* @param {string} type 类型
|
||||
* @param {string[]} uuids uuids
|
||||
*/
|
||||
'selection:selected'(event, type, uuids) {
|
||||
onEditorSelection(type, uuids);
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开预览面板
|
||||
*/
|
||||
'open-view-panel'() {
|
||||
PanelManager.openViewPanel();
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开设置面板
|
||||
*/
|
||||
'open-settings-panel'() {
|
||||
PanelManager.openSettingsPanel();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
*/
|
||||
'menu-check-update'() {
|
||||
checkUpdate(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 版本
|
||||
* @param {*} event
|
||||
*/
|
||||
'menu-version'(event) {
|
||||
openRepository();
|
||||
},
|
||||
|
||||
/**
|
||||
* 场景面板加载完成后
|
||||
* @param {*} event
|
||||
*/
|
||||
'scene:ready'(event) {
|
||||
// 自动检查更新
|
||||
const config = ConfigManager.get();
|
||||
if (config.autoCheckUpdate) {
|
||||
checkUpdate(false);
|
||||
}
|
||||
},
|
||||
|
||||
openViewPanel() {
|
||||
PanelManager.openViewPanel();
|
||||
},
|
||||
|
||||
load,
|
||||
/**
|
||||
* 打开设置面板
|
||||
*/
|
||||
openSettingsPanel() {
|
||||
PanelManager.openSettingsPanel();
|
||||
},
|
||||
|
||||
unload,
|
||||
/**
|
||||
* 检查更新
|
||||
*/
|
||||
menuCheckUpdate() {
|
||||
checkUpdate(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
menuVersion() {
|
||||
openRepository();
|
||||
},
|
||||
|
||||
/**
|
||||
* 场景编辑器就绪后
|
||||
*/
|
||||
onSceneReady() {
|
||||
// 自动检查更新
|
||||
const config = ConfigManager.get();
|
||||
if (config.autoCheckUpdate) {
|
||||
checkUpdate(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑器选中事件回调
|
||||
* @param {'asset' | 'node'} type 类型
|
||||
* @param {string} uuid uuid
|
||||
*/
|
||||
onSelectionSelect(type, uuid) {
|
||||
const uuids = EditorAdapter.Selection.getSelected(type);
|
||||
onSelectionSelect(type, uuids);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
const { dialog } = require('electron');
|
||||
const Fs = require('fs');
|
||||
const Path = require('path');
|
||||
const EditorAdapter = require('../common/editor-adapter');
|
||||
const { print, translate } = require('../eazax/editor-main-util');
|
||||
const MainEvent = require('../eazax/main-event');
|
||||
const PackageUtil = require('../eazax/package-util');
|
||||
const PanelManager = require('./panel-manager');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/**
|
||||
* 资源检索器
|
||||
*/
|
||||
const Opener = {
|
||||
|
||||
/**
|
||||
* 编辑器选择
|
||||
* @param {string} type
|
||||
* @param {'asset' | 'node'} type
|
||||
* @param {string[]} uuids
|
||||
*/
|
||||
async identifySelection(type, uuids) {
|
||||
// 选中资源
|
||||
if (type === 'asset') {
|
||||
if (type === 'asset') { // 选中资源
|
||||
Opener.identifyByUuids(uuids);
|
||||
}
|
||||
// 选中节点
|
||||
else if (type === 'node') {
|
||||
} else if (type === 'node') { // 选中节点
|
||||
const skeletonUuid = await Opener.querySkeletonOnNode(uuids[0]);
|
||||
if (skeletonUuid) {
|
||||
Opener.identifyByUuids([skeletonUuid]);
|
||||
@@ -38,9 +35,10 @@ const Opener = {
|
||||
* 检查编辑器当前选中
|
||||
*/
|
||||
checkEditorCurSelection() {
|
||||
const { type, id } = Editor.Selection.curGlobalActivate();
|
||||
if (type && id) {
|
||||
Opener.identifySelection(type, [id]);
|
||||
const type = EditorAdapter.Selection.getSelectedType(),
|
||||
uuids = EditorAdapter.Selection.getSelected(type);
|
||||
if (type && uuids && uuids.length > 0) {
|
||||
Opener.identifySelection(type, uuids);
|
||||
} else {
|
||||
Opener.updateView(null);
|
||||
}
|
||||
@@ -48,15 +46,21 @@ const Opener = {
|
||||
|
||||
/**
|
||||
* 查找节点上引用的骨骼资源
|
||||
* @param {string} uuid
|
||||
* @param {string} nodeUuid
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
querySkeletonOnNode(uuid) {
|
||||
return new Promise(res => {
|
||||
Editor.Scene.callSceneScript(PACKAGE_NAME, 'query-skeleton', uuid, (error, uuid) => {
|
||||
res(error ? null : uuid);
|
||||
});
|
||||
});
|
||||
async querySkeletonOnNode(nodeUuid) {
|
||||
const node = await Editor.Message.request('scene', 'query-node', nodeUuid);
|
||||
if (node && node['__comps__']) {
|
||||
const components = node['__comps__'];
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
if (components[i].type === 'sp.Skeleton') {
|
||||
const uuid = components[i].value.skeletonData.value.uuid;
|
||||
return (uuid !== '' ? uuid : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -85,40 +89,36 @@ const Opener = {
|
||||
* 通过 uuid 识别资源
|
||||
* @param {string[]} uuids
|
||||
*/
|
||||
identifyByUuids(uuids) {
|
||||
async identifyByUuids(uuids) {
|
||||
// 资源路径
|
||||
let spinePath, texturePath, atlasPath;
|
||||
let skeletonPath, texturePath, atlasPath;
|
||||
// 遍历选中的资源 uuid
|
||||
for (let i = 0; i < uuids.length; i++) {
|
||||
const assetInfo = Editor.assetdb.assetInfoByUuid(uuids[i]),
|
||||
{ type, path } = assetInfo;
|
||||
if (type === 'spine') {
|
||||
spinePath = path; // Spine 资源
|
||||
} else if (type === 'texture') {
|
||||
texturePath = path; // 纹理资源
|
||||
} else if (path.endsWith('.atlas') || path.endsWith('.txt')) {
|
||||
atlasPath = path; // 图集资源
|
||||
const assetInfo = await EditorAdapter.getAssetInfoByUuid(uuids[0]),
|
||||
{ type, file } = assetInfo;
|
||||
if (type === 'sp.SkeletonData') {
|
||||
skeletonPath = file; // 骨骼资源
|
||||
} else if (type === 'cc.ImageAsset') {
|
||||
texturePath = file; // 纹理资源
|
||||
} else if (file.endsWith('.atlas') || file.endsWith('.txt')) {
|
||||
atlasPath = file; // 图集资源
|
||||
}
|
||||
// 只识别一套资源
|
||||
if (spinePath && texturePath && atlasPath) {
|
||||
if (skeletonPath && texturePath && atlasPath) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 是否有选中 Spine 资源
|
||||
if (!spinePath) {
|
||||
// 未选中骨骼资源
|
||||
// if (!skeletonPath) {
|
||||
// return;
|
||||
// }
|
||||
// 无效
|
||||
if (!skeletonPath && !texturePath && !atlasPath) {
|
||||
Opener.updateView(null);
|
||||
return;
|
||||
}
|
||||
// 是否有选中图集资源
|
||||
if (!texturePath) {
|
||||
// 读取 Spine 资源的 meta 信息中获取关联的纹理资源
|
||||
const spineMeta = Editor.assetdb.loadMetaByPath(spinePath);
|
||||
if (spineMeta.textures[0]) {
|
||||
texturePath = Editor.assetdb.uuidToFspath(spineMeta.textures[0]);
|
||||
}
|
||||
}
|
||||
// 处理路径
|
||||
let paths = { spinePath, texturePath, atlasPath };
|
||||
const paths = { skeletonPath, texturePath, atlasPath };
|
||||
const assets = Opener.collectAssets(paths);
|
||||
Opener.updateView(assets);
|
||||
},
|
||||
@@ -129,7 +129,7 @@ const Opener = {
|
||||
*/
|
||||
identifyByPaths(paths) {
|
||||
// 资源路径
|
||||
let spinePath, texturePath, atlasPath;
|
||||
let skeletonPath, texturePath, atlasPath;
|
||||
// 遍历选中的文件路径
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const path = paths[i],
|
||||
@@ -137,7 +137,7 @@ const Opener = {
|
||||
switch (extname) {
|
||||
case '.json':
|
||||
case '.skel': {
|
||||
spinePath = path;
|
||||
skeletonPath = path;
|
||||
break;
|
||||
}
|
||||
case '.png': {
|
||||
@@ -151,65 +151,85 @@ const Opener = {
|
||||
}
|
||||
}
|
||||
// 只识别一套资源
|
||||
if (spinePath && texturePath && atlasPath) {
|
||||
if (skeletonPath && texturePath && atlasPath) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 是否有选中 spine 资源
|
||||
if (!spinePath) {
|
||||
print('warn', translate('noSkeleton'));
|
||||
// 未选中骨骼资源
|
||||
// if (!skeletonPath) {
|
||||
// print('warn', translate('noSkeleton'));
|
||||
// return;
|
||||
// }
|
||||
// 无效
|
||||
if (!skeletonPath && !texturePath && !atlasPath) {
|
||||
// print('warn', translate('noSkeleton'));
|
||||
return;
|
||||
}
|
||||
// 处理路径
|
||||
paths = { spinePath, texturePath, atlasPath };
|
||||
paths = { skeletonPath, texturePath, atlasPath };
|
||||
const assets = Opener.collectAssets(paths);
|
||||
Opener.updateView(assets);
|
||||
},
|
||||
|
||||
/**
|
||||
* 收集资源
|
||||
* @param {{ spinePath: string, texturePath: string, atlasPath: string }} paths 资源路径
|
||||
* @param {{ skeletonPath: string, texturePath: string, atlasPath: string }} paths 资源路径
|
||||
*/
|
||||
collectAssets(paths) {
|
||||
let { spinePath, texturePath, atlasPath } = paths;
|
||||
let { skeletonPath, texturePath, atlasPath } = paths;
|
||||
const testPath = skeletonPath || texturePath || atlasPath;
|
||||
// 骨骼资源
|
||||
if (!skeletonPath) {
|
||||
// 暴力查找
|
||||
skeletonPath = Opener.getRelatedFile(testPath, 'json');
|
||||
// 还没有的话再试试 skel 格式
|
||||
if (!skeletonPath) {
|
||||
skeletonPath = Opener.getRelatedFile(testPath, 'skel');
|
||||
}
|
||||
// 找不到骨骼啊
|
||||
if (!skeletonPath) {
|
||||
// print('warn', translate('noSkeleton'));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 纹理资源
|
||||
if (!texturePath) {
|
||||
// 暴力查找
|
||||
texturePath = Opener.getRelatedFile(spinePath, 'png');
|
||||
}
|
||||
// 找不到纹理啊
|
||||
if (!texturePath) {
|
||||
print('warn', translate('noTexture'));
|
||||
return null;
|
||||
texturePath = Opener.getRelatedFile(testPath, 'png');
|
||||
// 找不到纹理啊
|
||||
if (!texturePath) {
|
||||
print('warn', translate('noTexture'));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 图集资源
|
||||
if (!atlasPath) {
|
||||
// 暴力查找
|
||||
atlasPath = Opener.getRelatedFile(spinePath, 'atlas');
|
||||
atlasPath = Opener.getRelatedFile(testPath, 'atlas');
|
||||
// 还没有的话再试试 txt 格式
|
||||
if (!atlasPath) {
|
||||
atlasPath = Opener.getRelatedFile(spinePath, 'txt');
|
||||
atlasPath = Opener.getRelatedFile(testPath, 'txt');
|
||||
}
|
||||
// 还没有的话再试试 atlas.txt 格式
|
||||
if (!atlasPath) {
|
||||
atlasPath = Opener.getRelatedFile(spinePath, 'atlas.txt');
|
||||
atlasPath = Opener.getRelatedFile(testPath, 'atlas.txt');
|
||||
}
|
||||
// 找不到图集啊
|
||||
if (!atlasPath) {
|
||||
print('warn', translate('noAtlas'));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// 找不到图集啊
|
||||
if (!atlasPath) {
|
||||
print('warn', translate('noAtlas'));
|
||||
return null;
|
||||
}
|
||||
// 文件类型(json 或 skel)
|
||||
const spineType = Path.extname(spinePath);
|
||||
const skeletonType = Path.extname(skeletonPath);
|
||||
// 打包资源信息
|
||||
const assets = {
|
||||
// 目录路径
|
||||
dir: undefined,
|
||||
// 骨骼数据(JSON)
|
||||
json: (spineType === '.json') ? spinePath : undefined,
|
||||
json: (skeletonType === '.json') ? skeletonPath : undefined,
|
||||
// 骨骼数据(二进制)
|
||||
skel: (spineType === '.skel') ? spinePath : undefined,
|
||||
skel: (skeletonType === '.skel') ? skeletonPath : undefined,
|
||||
// 纹理
|
||||
png: texturePath,
|
||||
// 图集
|
||||
@@ -247,7 +267,7 @@ const Opener = {
|
||||
* @param {{ dir: string, json: string, atlas: string, png: string } | null} assets
|
||||
*/
|
||||
updateView(assets) {
|
||||
const webContents = PanelManager.getViewPanel();
|
||||
const webContents = PanelManager.getViewPanelWebContents();
|
||||
if (webContents) {
|
||||
MainEvent.send(webContents, 'assets-selected', assets);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ const { BrowserWindow } = require('electron');
|
||||
const { join } = require('path');
|
||||
const PackageUtil = require('../eazax/package-util');
|
||||
const { language, translate } = require('../eazax/editor-main-util');
|
||||
const { calcWindowPosition } = require('../eazax/window-util');
|
||||
const { calcWindowPositionByFocused } = require('../eazax/window-util');
|
||||
const EditorAdapter = require('../common/editor-adapter');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
@@ -15,22 +16,33 @@ const EXTENSION_NAME = translate('name');
|
||||
*/
|
||||
const PanelManager = {
|
||||
|
||||
/**
|
||||
* 预览面板的 WebContents
|
||||
* @type {Electron.WebContents}
|
||||
*/
|
||||
viewWebContents: null,
|
||||
|
||||
/**
|
||||
* 打开预览面板
|
||||
*/
|
||||
openViewPanel() {
|
||||
Editor.Panel.open(`${PACKAGE_NAME}.view`);
|
||||
EditorAdapter.Panel.open(`${PACKAGE_NAME}.view`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取预览面板
|
||||
* @returns {Electron.WebContents | null}
|
||||
* 关闭预览面板
|
||||
*/
|
||||
getViewPanel() {
|
||||
const panel = Editor.Panel.findWindow(`${PACKAGE_NAME}.view`);
|
||||
if (panel) {
|
||||
const webContents = panel.nativeWin.webContents;
|
||||
return webContents;
|
||||
closeViewPanel() {
|
||||
EditorAdapter.Panel.close(`${PACKAGE_NAME}.view`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取预览面板的 WebContents
|
||||
* @returns {Electron.WebContents}
|
||||
*/
|
||||
getViewPanelWebContents() {
|
||||
if (PanelManager.viewWebContents && !PanelManager.viewWebContents.isDestroyed()) {
|
||||
return PanelManager.viewWebContents;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -50,9 +62,9 @@ const PanelManager = {
|
||||
PanelManager.settings.show();
|
||||
return;
|
||||
}
|
||||
// 窗口高度和位置
|
||||
const winSize = [500, 290],
|
||||
winPos = calcWindowPosition(winSize, 'center');
|
||||
// 窗口尺寸和位置(macOS 标题栏高 28px)
|
||||
const winSize = [500, 350],
|
||||
winPos = calcWindowPositionByFocused(winSize, 'center');
|
||||
// 创建窗口
|
||||
const win = PanelManager.settings = new BrowserWindow({
|
||||
width: winSize[0],
|
||||
@@ -61,7 +73,6 @@ const PanelManager = {
|
||||
minHeight: winSize[1],
|
||||
x: winPos[0],
|
||||
y: winPos[1] - 100,
|
||||
useContentSize: true,
|
||||
frame: true,
|
||||
title: `${EXTENSION_NAME} | Cocos Creator`,
|
||||
autoHideMenuBar: true,
|
||||
@@ -78,7 +89,7 @@ const PanelManager = {
|
||||
contextIsolation: false,
|
||||
},
|
||||
});
|
||||
// 就绪后展示(避免闪烁)
|
||||
// 就绪后(展示,避免闪烁)
|
||||
win.on('ready-to-show', () => win.show());
|
||||
// 关闭后
|
||||
win.on('closed', () => (PanelManager.settings = null));
|
||||
@@ -86,7 +97,7 @@ const PanelManager = {
|
||||
win.webContents.on('before-input-event', (event, input) => {
|
||||
if (input.key === 'Escape') PanelManager.closeSettingsPanel();
|
||||
});
|
||||
// 调试用的 devtools(detach 模式需要取消失焦自动关闭)
|
||||
// 调试用的 devtools
|
||||
// win.webContents.openDevTools({ mode: 'detach' });
|
||||
// 加载页面
|
||||
const path = join(__dirname, '../renderer/settings/index.html');
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* 查询节点上的骨骼资源
|
||||
* @param {*} event
|
||||
* @param {string} uuid
|
||||
* @returns
|
||||
*/
|
||||
'query-skeleton'(event, uuid) {
|
||||
// 获取节点
|
||||
const node = cc.engine.getInstanceById(uuid);
|
||||
if (!node) {
|
||||
event.reply(null, null);
|
||||
return;
|
||||
}
|
||||
// 获取节点上的骨骼组件
|
||||
const spine = node.getComponent('sp.Skeleton');
|
||||
if (!spine) {
|
||||
event.reply(null, null);
|
||||
return;
|
||||
}
|
||||
// 获取骨骼数据
|
||||
const skeletonData = spine.skeletonData;
|
||||
if (!skeletonData) {
|
||||
event.reply(null, null);
|
||||
return;
|
||||
}
|
||||
// 返回资源的 uuid
|
||||
event.reply(null, skeletonData._uuid);
|
||||
},
|
||||
|
||||
};
|
||||
@@ -40,11 +40,21 @@
|
||||
<input v-model="customKey" :placeholder="t('customKeyPlaceholder')" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 窗口置顶 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('alwaysOnTop') }}</span>
|
||||
<span class="tooltip">{{ t('alwaysOnTopTooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="alwaysOnTop" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 自动检查更新 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('autoCheck') }}</span>
|
||||
<span class="tooltip">{{ t('autoCheckTooltip') }}</span>
|
||||
<span class="text">{{ t('autoCheckUpdate') }}</span>
|
||||
<span class="tooltip">{{ t('autoCheckUpdateTooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="autoCheckUpdate" />
|
||||
|
||||
@@ -39,6 +39,8 @@ const App = {
|
||||
const selectKey = ref('');
|
||||
// 自定义
|
||||
const customKey = ref('');
|
||||
// 窗口置顶
|
||||
const alwaysOnTop = ref(true);
|
||||
// 自动检查更新
|
||||
const autoCheckUpdate = ref(false);
|
||||
|
||||
@@ -66,27 +68,31 @@ const App = {
|
||||
*/
|
||||
function getConfig() {
|
||||
const config = ConfigManager.get();
|
||||
if (!config) return;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
// 窗口置顶
|
||||
alwaysOnTop.value = config.alwaysOnTop;
|
||||
// 自动检查更新
|
||||
autoCheckUpdate.value = config.autoCheckUpdate;
|
||||
// 快捷键
|
||||
const hotkey = config.hotkey;
|
||||
if (!hotkey || hotkey === '') {
|
||||
const shortcutKey = config.shortcutKey;
|
||||
if (!shortcutKey || shortcutKey === '') {
|
||||
selectKey.value = '';
|
||||
customKey.value = '';
|
||||
return;
|
||||
}
|
||||
// 预设快捷键
|
||||
for (let i = 0, l = presets.length; i < l; i++) {
|
||||
if (presets[i].key === hotkey) {
|
||||
selectKey.value = hotkey;
|
||||
for (let i = 0, l = presets.value.length; i < l; i++) {
|
||||
if (presets.value[i].key === shortcutKey) {
|
||||
selectKey.value = shortcutKey;
|
||||
customKey.value = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 自定义快捷键
|
||||
selectKey.value = 'custom';
|
||||
customKey.value = hotkey;
|
||||
customKey.value = shortcutKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,8 +100,9 @@ const App = {
|
||||
*/
|
||||
function setConfig() {
|
||||
const config = {
|
||||
alwaysOnTop: alwaysOnTop.value,
|
||||
autoCheckUpdate: autoCheckUpdate.value,
|
||||
hotkey: null,
|
||||
shortcutKey: null,
|
||||
};
|
||||
if (selectKey.value === 'custom') {
|
||||
// 自定义输入是否有效
|
||||
@@ -109,12 +116,14 @@ const App = {
|
||||
EditorRendererKit.print('warn', translate('quoteError'));
|
||||
return;
|
||||
}
|
||||
config.hotkey = customKey.value;
|
||||
config.shortcutKey = customKey.value;
|
||||
} else {
|
||||
config.hotkey = selectKey.value;
|
||||
config.shortcutKey = selectKey.value;
|
||||
}
|
||||
// 保存到本地
|
||||
ConfigManager.set(config);
|
||||
// 重新加载扩展
|
||||
RendererEvent.send('reload');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,6 +173,7 @@ const App = {
|
||||
presets,
|
||||
selectKey,
|
||||
customKey,
|
||||
alwaysOnTop,
|
||||
autoCheckUpdate,
|
||||
repositoryUrl,
|
||||
packageName,
|
||||
|
||||
@@ -1,57 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
const { readFileSync } = require('fs');
|
||||
const { join } = require('path');
|
||||
|
||||
// ⚠️ 2.4.5 以上版本可以直接导入
|
||||
// 但是 2.4.5 以下版本无法正常访问 __dirname
|
||||
// const Vue = require('../../../lib/vue.global.prod');
|
||||
// const App = require('./index');
|
||||
// 插件内置的 Vue
|
||||
const Vue = require('../../../lib/vue.global.prod');
|
||||
// 面板代码
|
||||
const App = require('./index');
|
||||
|
||||
// ⚠️ 2.4.5 以下版本只能通过 Editor.url 来获取插件路径
|
||||
const PACKAGE_NAME = 'ccc-skeleton-viewer';
|
||||
const PACKAGE_PATH = Editor.url(`packages://${PACKAGE_NAME}/`);
|
||||
const DIR_PATH = join(PACKAGE_PATH, 'src/renderer/view/');
|
||||
// 面板 Vue 实例
|
||||
let app = null;
|
||||
|
||||
const Vue = require(join(PACKAGE_PATH, 'lib/vue.global.prod'));
|
||||
const App = require(join(DIR_PATH, 'index'));
|
||||
// html 文本
|
||||
exports.template = readFileSync(join(__dirname, 'index.html'), 'utf8');
|
||||
|
||||
// 创建面板
|
||||
Editor.Panel.extend({
|
||||
// 样式文本
|
||||
exports.style = '';
|
||||
|
||||
/** HTML */
|
||||
// template: readFileSync(join(__dirname, 'index.html'), 'utf8'),
|
||||
template: readFileSync(join(DIR_PATH, 'index.html'), 'utf8'),
|
||||
// 渲染后 html 选择器
|
||||
exports.$ = {
|
||||
app: '#app',
|
||||
};
|
||||
|
||||
/**
|
||||
* 面板渲染成功
|
||||
*/
|
||||
ready() {
|
||||
const root = this.shadowRoot;
|
||||
// 加载样式表
|
||||
// loadCss(root, join(__dirname, '../../eazax/css/cocos-tag.css'));
|
||||
// loadCss(root, join(__dirname, '../../eazax/css/cocos-class.css'));
|
||||
// loadCss(root, join(__dirname, 'index.css'));
|
||||
loadCSS(root, join(PACKAGE_PATH, 'src/eazax/css/cocos-tag.css'));
|
||||
loadCSS(root, join(PACKAGE_PATH, 'src/eazax/css/cocos-class.css'));
|
||||
loadCSS(root, join(DIR_PATH, 'index.css'));
|
||||
// 先替换掉编辑器内置的 Vue
|
||||
const oldVue = window.Vue;
|
||||
window.Vue = Vue;
|
||||
// 创建实例
|
||||
const app = Vue.createApp(App);
|
||||
// 挂载
|
||||
app.mount(root);
|
||||
// 把编辑器的 Vue 换回去
|
||||
window.Vue = oldVue;
|
||||
},
|
||||
// 面板上的方法
|
||||
exports.methods = {};
|
||||
|
||||
});
|
||||
// 面板上触发的事件
|
||||
exports.listeners = {};
|
||||
|
||||
// 当面板渲染成功后触发
|
||||
exports.ready = async function () {
|
||||
const root = this.$.app.parentNode;
|
||||
|
||||
// 加载样式表
|
||||
loadCss(root, join(__dirname, '../../eazax/css/cocos-tag.css'));
|
||||
loadCss(root, join(__dirname, '../../eazax/css/cocos-class.css'));
|
||||
loadCss(root, join(__dirname, 'index.css'));
|
||||
|
||||
// 先替换掉编辑器内置的 Vue(理论上 3.x 编辑器不内置 Vue)
|
||||
const builtinVue = window.Vue;
|
||||
window.Vue = Vue;
|
||||
|
||||
// 创建 Vue 实例
|
||||
app = Vue.createApp(App);
|
||||
// 挂载 Vue 实例
|
||||
app.mount(root);
|
||||
|
||||
// 把编辑器的 Vue 换回去
|
||||
window.Vue = builtinVue;
|
||||
};
|
||||
|
||||
// 尝试关闭面板的时候触发
|
||||
exports.beforeClose = async function () {
|
||||
// 卸载 Vue 实例
|
||||
app.unmount();
|
||||
};
|
||||
|
||||
// 当面板实际关闭后触发
|
||||
exports.close = async function () { };
|
||||
|
||||
/**
|
||||
* 加载样式表
|
||||
* @param {HTMLElement} root 根元素
|
||||
* @param {string} path CSS 文件路径
|
||||
*/
|
||||
function loadCSS(root, path) {
|
||||
function loadCss(root, path) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
#app {
|
||||
--background-color: #474747;
|
||||
--content-color: #2f2f2f;
|
||||
--font-color: #bdbdbd;
|
||||
--hover-color: #09f;
|
||||
--active-color: #fd942b;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #454545;
|
||||
background-color: var(--background-color);
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
color: #bdbdbd;
|
||||
color: var(--font-color);
|
||||
animation: fade-in 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@@ -31,7 +39,7 @@
|
||||
.view {
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
background-color: #4c4c4c;
|
||||
background-color: var(--content-color);
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
@@ -40,7 +48,7 @@
|
||||
.canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #4c4c4c;
|
||||
background-color: var(--content-color);
|
||||
}
|
||||
|
||||
/* 版本号 */
|
||||
@@ -52,7 +60,8 @@
|
||||
right: 6px;
|
||||
line-height: 16px;
|
||||
font-size: 11px;
|
||||
color: #bdbdbd;
|
||||
color: var(--font-color);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 按钮容器 */
|
||||
@@ -76,7 +85,7 @@
|
||||
.button {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: #bdbdbd;
|
||||
color: var(--font-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -86,12 +95,12 @@
|
||||
/* 信息,按钮:虚指 */
|
||||
.info:hover,
|
||||
.button:hover {
|
||||
color: #09f;
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
/* 按钮:激活 */
|
||||
.button:active {
|
||||
color: #fd942b;
|
||||
color: var(--active-color);
|
||||
}
|
||||
|
||||
/* 信息,按钮 图标 */
|
||||
@@ -117,6 +126,7 @@
|
||||
margin-right: 2px;
|
||||
line-height: 12px;
|
||||
font-size: 11px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* 位置 按钮 */
|
||||
|
||||
@@ -1,159 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div ref="app" id="app" style="opacity: 0;">
|
||||
<!-- 内容布局 -->
|
||||
<div ref="layout" class="layout">
|
||||
<!-- 预览区 -->
|
||||
<div class="view">
|
||||
<!-- 画布 -->
|
||||
<canvas ref="canvas" class="canvas"></canvas>
|
||||
<!-- 按钮容器 -->
|
||||
<div class="buttons">
|
||||
<!-- 资源信息按钮 -->
|
||||
<div class="button" :title="assetsInfo" @click="onInfoBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M4.395 25.605c-2.825-2.731-4.579-6.556-4.579-10.789 0-8.284 6.716-15 15-15 4.234 0 8.058 1.754 10.785 4.575l0.004 0.004c2.605 2.695 4.211 6.37 4.211 10.421 0 8.284-6.716 15-15 15-4.050 0-7.726-1.605-10.425-4.215l0.004 0.004zM13.5 16.5v6h3v-9h-3v3zM13.5 7.5v3h3v-3h-3z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 选择资源按钮 -->
|
||||
<div class="button" :title="t('selectAssets')" @click="onSelectBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M15 30c-8.284 0-15-6.716-15-15s6.716-15 15-15v0c8.284 0 15 6.716 15 15s-6.716 15-15 15v0zM11.82 11.82l-5.31 11.67 11.67-5.31 5.31-11.67-11.67 5.31zM15 16.5c-0.828 0-1.5-0.672-1.5-1.5s0.672-1.5 1.5-1.5v0c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5v0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 重置按钮 -->
|
||||
<div class="button" :title="t('reset')" @click="onResetBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M4.395 25.605c-2.825-2.731-4.579-6.556-4.579-10.789 0-8.284 6.716-15 15-15 4.234 0 8.058 1.754 10.785 4.575l0.004 0.004c2.605 2.695 4.211 6.37 4.211 10.421 0 8.284-6.716 15-15 15-4.050 0-7.726-1.605-10.425-4.215l0.004 0.004zM17.1 15l4.245-4.245-2.115-2.115-4.23 4.245-4.245-4.245-2.115 2.115 4.245 4.245-4.245 4.245 2.115 2.115 4.245-4.245 4.245 4.245 2.115-2.115-4.245-4.245z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div ref="app" id="app" style="opacity: 0;">
|
||||
<!-- 内容布局 -->
|
||||
<div ref="layout" class="layout">
|
||||
<!-- 预览区 -->
|
||||
<div class="view">
|
||||
<!-- 画布 -->
|
||||
<canvas ref="canvas" class="canvas"></canvas>
|
||||
<!-- 按钮容器 -->
|
||||
<div class="buttons">
|
||||
<!-- 资源信息按钮 -->
|
||||
<div class="button" :title="assetsInfo" @click="onInfoBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M4.395 25.605c-2.825-2.731-4.579-6.556-4.579-10.789 0-8.284 6.716-15 15-15 4.234 0 8.058 1.754 10.785 4.575l0.004 0.004c2.605 2.695 4.211 6.37 4.211 10.421 0 8.284-6.716 15-15 15-4.050 0-7.726-1.605-10.425-4.215l0.004 0.004zM13.5 16.5v6h3v-9h-3v3zM13.5 7.5v3h3v-3h-3z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 运行时版本 -->
|
||||
<div class="version">
|
||||
<span :title="t('spineRuntime') + ': ' + version">{{ t('spineRuntime') }}: {{ version }}</span>
|
||||
<!-- 选择资源按钮 -->
|
||||
<div class="button" :title="t('selectAssets')" @click="onSelectBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M15 30c-8.284 0-15-6.716-15-15s6.716-15 15-15v0c8.284 0 15 6.716 15 15s-6.716 15-15 15v0zM11.82 11.82l-5.31 11.67 11.67-5.31 5.31-11.67-11.67 5.31zM15 16.5c-0.828 0-1.5-0.672-1.5-1.5s0.672-1.5 1.5-1.5v0c0.828 0 1.5 0.672 1.5 1.5s-0.672 1.5-1.5 1.5v0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 颜色 -->
|
||||
<div class="color">
|
||||
<!-- 取色器 -->
|
||||
<input type="color" v-model="canvasColor" :title="t('canvasColor')" />
|
||||
</div>
|
||||
<!-- 位置 -->
|
||||
<div class="position">
|
||||
<!-- 文本 -->
|
||||
<div class="label" :title="offset">{{ offset }}</div>
|
||||
<!-- 复位按钮 -->
|
||||
<div class="button" :title="t('reposition')" @click="onRepositionBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M15 30s-10.5-13.695-10.5-19.5c0-5.799 4.701-10.5 10.5-10.5s10.5 4.701 10.5 10.5v0c0 5.805-10.5 19.5-10.5 19.5zM15 13.5c1.657 0 3-1.343 3-3s-1.343-3-3-3v0c-1.657 0-3 1.343-3 3s1.343 3 3 3v0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 重置按钮 -->
|
||||
<div class="button" :title="t('reset')" @click="onResetBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M4.395 25.605c-2.825-2.731-4.579-6.556-4.579-10.789 0-8.284 6.716-15 15-15 4.234 0 8.058 1.754 10.785 4.575l0.004 0.004c2.605 2.695 4.211 6.37 4.211 10.421 0 8.284-6.716 15-15 15-4.050 0-7.726-1.605-10.425-4.215l0.004 0.004zM17.1 15l4.245-4.245-2.115-2.115-4.23 4.245-4.245-4.245-2.115 2.115 4.245 4.245-4.245 4.245 2.115 2.115 4.245-4.245 4.245 4.245 2.115-2.115-4.245-4.245z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 选项区 -->
|
||||
<div ref="properties" class="properties">
|
||||
<!-- 预览缩放 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('viewScale') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="number" step="0.1" v-model="viewScale" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 皮肤 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('skin') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<select v-model="skin">
|
||||
<option v-for="name in skins" :key="name" :value="name">{{ name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 动画 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('animation') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<select v-model="animation">
|
||||
<option v-for="name in animations" :key="name" :value="name">{{ name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 循环 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('loop') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="loop" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 预乘 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('premultipliedAlpha') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="premultipliedAlpha" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 时间缩放 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('timeScale') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="number" step="0.1" v-model="timeScale" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制骨骼 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawBones') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawBones" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制包围盒 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawBoundingBoxes') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawBoundingBoxes" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制网格三角形 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawMeshTriangles') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawMeshTriangles" />
|
||||
</div>
|
||||
<!-- 运行时版本 -->
|
||||
<div class="version">
|
||||
<span :title="t('spineRuntime') + ': ' + version">{{ t('spineRuntime') }}: {{ version }}</span>
|
||||
</div>
|
||||
<!-- 颜色 -->
|
||||
<div class="color">
|
||||
<!-- 取色器 -->
|
||||
<input type="color" v-model="canvasColor" :title="t('canvasColor')" />
|
||||
</div>
|
||||
<!-- 位置 -->
|
||||
<div class="position">
|
||||
<!-- 文本 -->
|
||||
<div class="label" :title="offset">{{ offset }}</div>
|
||||
<!-- 复位按钮 -->
|
||||
<div class="button" :title="t('reposition')" @click="onRepositionBtnClick">
|
||||
<svg class="icon" viewBox="0 0 30 30">
|
||||
<path fill="currentColor"
|
||||
d="M15 30s-10.5-13.695-10.5-19.5c0-5.799 4.701-10.5 10.5-10.5s10.5 4.701 10.5 10.5v0c0 5.805-10.5 19.5-10.5 19.5zM15 13.5c1.657 0 3-1.343 3-3s-1.343-3-3-3v0c-1.657 0-3 1.343-3 3s1.343 3 3 3v0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 选项区 -->
|
||||
<div ref="properties" class="properties">
|
||||
<!-- 预览缩放 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('viewScale') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="number" step="0.1" v-model="viewScale" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 皮肤 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('skin') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<select v-model="skin">
|
||||
<option v-for="name in skins" :key="name" :value="name">{{ name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 动画 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('animation') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<select v-model="animation">
|
||||
<option v-for="name in animations" :key="name" :value="name">{{ name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 循环 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('loop') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="loop" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 预乘 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('premultipliedAlpha') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="premultipliedAlpha" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 时间缩放 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('timeScale') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="number" step="0.1" v-model="timeScale" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制骨骼 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawBones') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawBones" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制包围盒 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawBoundingBoxes') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawBoundingBoxes" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 绘制网格三角形 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('drawMeshTriangles') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="drawMeshTriangles" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<br>
|
||||
</div>
|
||||
File diff suppressed because it is too large
Load Diff
1053
typings/cocos/editor.d.ts
vendored
1053
typings/cocos/editor.d.ts
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user