panel_ex.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /*
  2. 面板扩展
  3. 功能: 下拉框转跳文件,转跳场景,重命名,打开项目到外部编辑器...
  4. */
  5. 'use strict';
  6. const path = require('path');
  7. const md5 = require('md5');
  8. const fs = require('fs');
  9. const fe = Editor.require('packages://simple-code/tools/tools.js');
  10. const cfg = Editor.require('packages://simple-code/config.js');
  11. const exec = require('child_process').exec
  12. const inputType = {"text":1,"password":1,"number":1,"date":1,"color":1,"range":1,"month":1,"week":1,"time":1,"email":1,"search":1,"url":1,"textarea":1}
  13. module.exports = {
  14. /** @type import('../../panel/vs-panel/vs-panel-base') */
  15. parent : null,
  16. // 面板初始化
  17. onLoad(parent){
  18. // index.js 对象
  19. this.parent = parent;
  20. // 0代表只有非编辑状态时可用,1代表仅在在文本编辑状态使用,2全局不受影响
  21. // 键盘事件:添加节点组件
  22. this.parent.addKeybodyEventByName('addCompToScene',(e)=>
  23. {
  24. if ( Editor.Selection.curSelection("node").length> 0){
  25. this.openodeCompList();
  26. e.preventDefault();// 吞噬捕获事件
  27. }
  28. },0)
  29. // 键盘事件:全局搜索文本
  30. this.parent.addKeybodyEventByName('openGlobalSearch',(e)=>
  31. {
  32. this.openGlobalSearch()
  33. e.preventDefault();// 吞噬捕获事件
  34. },1)
  35. // 键盘事件:批量插入预制节点
  36. this.parent.addKeybodyEventByName('insertPrefab',(e)=>
  37. {
  38. if ( !this.inputTypeChk(e) && Editor.Selection.curSelection("node").length> 0){
  39. this.openPrefabList();
  40. e.preventDefault();// 吞噬捕获事件
  41. }
  42. },0)
  43. // 键盘事件:切换场景
  44. this.parent.addKeybodyEventByName('gotoAnything',(e)=>
  45. {
  46. if ( !this.inputTypeChk(e)){
  47. this.searchCmd("findFileAndOpen");
  48. e.preventDefault();// 吞噬捕获事件
  49. }
  50. },0)
  51. // 绑定页面全局快捷键事件,注意: 区分大小写 Control = ctrl
  52. this.parent.addKeybodyEventByName('gotoScriptFile',(e)=>
  53. {
  54. // 搜索转跳
  55. this.searchCmd("findJsFileAndOpen");
  56. e.preventDefault();// 吞噬捕获事件
  57. return false;
  58. },2)
  59. // 绑定页面全局快捷键事件,注意: 区分大小写 Control = ctrl
  60. this.parent.addKeybodyEventByName('findFileGoto',(e)=>
  61. {
  62. // 搜索转跳
  63. this.searchCmd("findFileGoto");
  64. e.preventDefault();// 吞噬捕获事件
  65. return false;
  66. },0)
  67. // 键盘事件:搜索节点
  68. this.parent.addKeybodyEventByName('findNodes',(e)=>
  69. {
  70. if ( !this.inputTypeChk(e)){
  71. setTimeout(()=>{
  72. this.openFindNode()
  73. },1)
  74. }
  75. },0);
  76. },
  77. // 不是输入状态是时
  78. inputTypeChk(e){
  79. if (e.path[0] ){
  80. let type = e.path[0].type ;
  81. if ( inputType[type]){
  82. return true
  83. }
  84. }
  85. },
  86. // 打开下拉搜索框
  87. searchCmd(cmd)
  88. {
  89. // 下拉框选中后操作事件
  90. let onSearchAccept = (cmd,data)=>
  91. {
  92. if (cmd == "findFileGoto")
  93. {
  94. // 高亮资源管理器
  95. Editor.Ipc.sendToAll('assets:hint', data.item.uuid)
  96. Editor.Selection.select('asset', data.item.uuid)
  97. this.parent.openActiveFile()
  98. }else //if(cmd == "findFileAndOpen")
  99. {
  100. if (data.item.extname == ".prefab") {
  101. Editor.Ipc.sendToAll('scene:enter-prefab-edit-mode', data.item.uuid);
  102. }
  103. if (data.item.extname == ".fire") {
  104. Editor.Ipc.sendToAll('scene:open-by-uuid', data.item.uuid);
  105. }else{
  106. if(data.item.uuid == 'outside'){
  107. this.parent.openOutSideFile(data.item.meta,true)
  108. }else{
  109. Editor.Selection.select('asset', data.item.uuid)
  110. setTimeout(()=>this.parent.openActiveFile(true),50)
  111. }
  112. }
  113. }
  114. }
  115. if (cmd == "findFileGoto")
  116. {
  117. // 打开搜索框: 文件定位转跳
  118. let fileList = []
  119. this.parent.file_list_buffer.forEach((v)=>
  120. {
  121. if (v.uuid != 'outside'){
  122. fileList.push(v)
  123. }
  124. });
  125. this.parent.ace.openSearchBox("",fileList,(data)=>onSearchAccept(cmd,data),null,null,'findFileGoto');
  126. }else if (cmd == "findFileAndOpen")
  127. {
  128. // 打开场景转跳
  129. let fileList = []
  130. this.parent.file_list_buffer.forEach((v)=>
  131. {
  132. // 过滤文件: 特定的文件才能打开
  133. let extname = v.extname.substr(1);
  134. if (extname == "prefab" || extname == "fire" || this.parent.FILE_OPEN_TYPES[extname]){
  135. fileList.push(v)
  136. }
  137. });
  138. // 打开搜索框
  139. this.parent.ace.openSearchBox("",fileList,(data)=>onSearchAccept(cmd,data),null,null,'findFileAndOpen');
  140. }else if (cmd == "findJsFileAndOpen")
  141. {
  142. // 打开场景转跳
  143. let fileList = []
  144. this.parent.file_list_buffer.forEach((v)=>
  145. {
  146. // 过滤文件: 特定的文件才能打开
  147. let extname = v.extname.substr(1);
  148. if (this.parent.FILE_OPEN_TYPES[extname]){
  149. fileList.push(v)
  150. }
  151. });
  152. // 打开搜索框
  153. this.parent.ace.openSearchBox("",fileList,(data)=>onSearchAccept(cmd,data),null,null,'findJsFileAndOpen');
  154. }
  155. },
  156. // 搜索选中节点
  157. openFindNode(){
  158. let uuid_list = Editor.Selection.curSelection('node');
  159. if(uuid_list.length == 0) return Editor.info("请选中节点后再继续操作");
  160. let node_uuid = uuid_list[0];
  161. let sch_id ;
  162. // 修改搜索框时,通过该函数读取显示的实时显示下拉列表内容, cmdLine为输入文本框对象
  163. let onCompletionsFunc = (cmdLine)=>{
  164. let name = cmdLine.getValue();
  165. if (sch_id) {
  166. clearTimeout(sch_id);
  167. }
  168. sch_id = setTimeout(()=>
  169. {
  170. if(name != "")
  171. {
  172. sch_id = null;
  173. Editor.Scene.callSceneScript('simple-code', 'select-node-by-name',{name:name,parent_uuid:node_uuid});
  174. }
  175. },400);
  176. return ["请输入需要批量选中的node名字"];
  177. }
  178. // 选中后处理
  179. let onAccept = (data,cmdLine)=>{
  180. }
  181. // 显示下拉框
  182. this.parent.ace.openSearchBox("",[],onAccept,onCompletionsFunc)
  183. },
  184. getFileName(path){
  185. let s_i = path.lastIndexOf("/")+1
  186. let e_i = path.lastIndexOf(".")
  187. e_i = e_i < s_i ? -1 :e_i
  188. let name = path.substr(s_i, e_i == -1 ? undefined : e_i - s_i )
  189. let suffix = e_i == -1 ? "" : path.substr(e_i)
  190. return {name,suffix,dir_path:path.substr(0,s_i)}
  191. },
  192. // 面板销毁
  193. onDestroy(){
  194. },
  195. // 打开组件列表
  196. openodeCompList(){
  197. // 下拉框选中后操作事件
  198. let onSearchAccept = (data)=>
  199. {
  200. // 获得选中的节点
  201. Editor.Scene.callSceneScript('simple-code', 'set-node-comp' ,data.item.value, (err, args)=>
  202. {
  203. });
  204. }
  205. Editor.Scene.callSceneScript('simple-code', 'get-comps' ,"", (err, args)=>
  206. {
  207. // 打开搜索框: 文件定位转跳
  208. let list = JSON.parse(args)
  209. this.parent.ace.openSearchBox("",list,(data)=>onSearchAccept(data),null,null,'openodeCompList');
  210. });
  211. },
  212. // 打开预制节点列表
  213. openPrefabList(){
  214. // 下拉框选中后操作事件
  215. let onSearchAccept = (data)=>
  216. {
  217. // 获得选中的节点
  218. Editor.Scene.callSceneScript('simple-code', 'add-prefab' ,data.item, (err, args)=>
  219. {
  220. });
  221. }
  222. let list = []
  223. this.parent.file_list_buffer.forEach((v,i)=>{
  224. if (v.extname == ".prefab"){
  225. // v = {
  226. // extname: result.extname,//格式
  227. // value: name == "" ? url : name ,
  228. // meta:  url,
  229. // score: 0,//搜索优先级
  230. // matchMask: 0,
  231. // exactMatch: 1,
  232. // uuid:result.uuid,
  233. // };
  234. list.push( v );
  235. }
  236. })
  237. // 打开搜索框
  238. this.parent.ace.openSearchBox("",list,(data)=>onSearchAccept(data),null,null,'openPrefabList');
  239. },
  240. // 全局搜索的数据
  241. findAllMatches (searchText) {
  242. let result = []
  243. if (!searchText) {
  244. return result;
  245. }
  246. //注意如果你一个model都没有注册的话,这里什么都拿不到
  247. //举个例子啊,下面将一个路径为filePath,语言为lang,文件内容为fileContent的本地文件注册为model
  248. //monaco.editor.createModel(fileContent, lang, monaco.Uri.file(filePath))
  249. let find_model = this.parent.monaco.editor.getModel('inmemory://model/1')
  250. if(!find_model) find_model = this.parent.monaco.editor.createModel('','markdown',this.parent.monaco.Uri.parse('inmemory://model/1'))
  251. for (const fsPath in this.parent.file_list_map)
  252. {
  253. const item = this.parent.file_list_map[fsPath];
  254. if(item.data == null){
  255. continue;
  256. }
  257. let file_name = fsPath.substr(fsPath.lastIndexOf('/'))
  258. if(file_name.indexOf('.d.ts') != -1) continue;
  259. let uri = Editor.monaco.Uri.parse(this.parent.fileMgr.fsPathToModelUrl(fsPath))
  260. let model = Editor.monaco.editor.getModel(uri);
  261. let codeText = item.data;
  262. if(model){
  263. codeText = model.getValue();
  264. }
  265. find_model.setValueNotUndo(codeText)
  266. for (let match of find_model.findMatches(searchText))
  267. {
  268. let text = find_model.getLineContent(match.range.startLineNumber);
  269. for (let i = 0; i < text.length; i++)
  270. {
  271. const c = text[i];
  272. if(c!=" " && c!=" "){
  273. text = text.substr(i);
  274. break;
  275. }
  276. }
  277. result.push({
  278. meta : text,
  279. uri : uri,
  280. fsPath:fsPath,
  281. value: file_name,
  282. range: match.range,
  283. score:0,
  284. })
  285. }
  286. }
  287. return result
  288. },
  289. // 全局搜索
  290. openGlobalSearch(){
  291. this.parent.ace.openSearchBox("",[],(data,cmdLine)=>
  292. {
  293. let searchText = cmdLine.getValue();
  294. this.showGlobalSearchListView(searchText)
  295. },()=>{
  296. return [fe.translate("global-search-hint")] // 请输入全局搜索内容
  297. });
  298. },
  299. showGlobalSearchListView(searchText){
  300. let result = this.findAllMatches(searchText);
  301. let is_has = result.length;
  302. result = is_has ? result : [fe.translate("no-relevant-content-found")] // '未找到相关内容'
  303. // 下拉框选中后操作事件
  304. let onSearchAccept = (data,cmdLine)=>
  305. {
  306. if(is_has && data.item) Editor.monaco.sendEvent('vs-open-file-tab',{fsPath:data.item.fsPath,uri:data.item.uri,selection:data.item.range});
  307. else this.openGlobalSearch()
  308. }
  309. // 修改搜索框时,通过该函数读取显示的实时显示下拉列表内容, cmdLine为输入文本框对象
  310. let onCompletionsFunc = (cmdLine)=>{
  311. return result;
  312. }
  313. this.parent.ace.openSearchBox(searchText,[],(data,cmdLine)=>onSearchAccept(data,cmdLine),(cmdLine)=>onCompletionsFunc(cmdLine))
  314. },
  315. /************* 事件 *************/
  316. messages:{
  317. // 添加组件
  318. 'addNodeComp'()
  319. {
  320. this.openodeCompList();
  321. },
  322. // 批量插入预制节点
  323. 'addPrefab'(){
  324. this.openPrefabList();
  325. },
  326. // 通过项目目录打开新项目
  327. 'openProject'(event,type){
  328. // 下拉框选中后操作事件
  329. let onSearchAccept = (data)=>
  330. {
  331. let dir_path = data.item.meta
  332. if (type == "dir")
  333. {
  334. // 打开目录
  335. exec( (Editor.isWin32 ? "start " : "open ")+dir_path )
  336. }else if (type == "editor")
  337. {
  338. // 打开项目到外部代码编辑器
  339. exec( (Editor.isWin32 ? '"'+cfg.editorPath.win+'"' :'"'+ cfg.editorPath.mac+'"')+" "+dir_path)
  340. // 打开项目从新creator
  341. if (Editor.isWin32){
  342. exec('"'+cfg.editorPath.win+'" '+dir_path)
  343. }else{
  344. // Mac
  345. exec("\""+cfg.editorPath.mac+"\" "+dir_path+"");
  346. }
  347. }else if (type == "creator")
  348. {
  349. // 打开项目从新creator
  350. if (Editor.isWin32){
  351. let appPath = Editor.appPath.substr(0,Editor.appPath.lastIndexOf(path.sep))
  352. appPath = appPath.substr(0,appPath.lastIndexOf(path.sep))
  353. appPath = '"'+ appPath + path.sep+'CocosCreator.exe"'+ ' --path '
  354. exec( appPath+dir_path)
  355. }else{
  356. // Mac
  357. let appPath = Editor.appPath.substr(0,Editor.appPath.lastIndexOf(path.sep))
  358. appPath.substr(0,appPath.lastIndexOf(path.sep))
  359. exec("nohup "+appPath+" "+dir_path+" >/dev/null 2>&1 &")
  360. }
  361. }
  362. Editor.log("正在执行打开操作:"+dir_path)
  363. }
  364. // 获得总项目目录位置: 当前项目上级目录
  365. let root_path = Editor.url("db://assets/")
  366. root_path = root_path.substr(0,root_path.lastIndexOf(path.sep))
  367. root_path = root_path.substr(0,root_path.lastIndexOf(path.sep))
  368. // 所有项目的列表
  369. let dirList = fe.getDirList(root_path,[]);
  370. let list = []
  371. dirList.forEach((dir_path)=>
  372. {
  373. list.push( this.parent.getItem( dir_path.substr(dir_path.lastIndexOf(path.sep)+1) ,dir_path,0) )
  374. })
  375. // 打开搜索框: 文件定位转跳
  376. this.parent.ace.openSearchBox("",list,(data)=>onSearchAccept(data));
  377. },
  378. // 快捷键打开当前选中文件/节点进入编辑
  379. 'custom-cmd' (event,info) {
  380. if(info.cmd == "findFileAndOpen")
  381. {
  382. // 下拉框打开场景或预制节点
  383. this.searchCmd(info.cmd)
  384. }else if(info.cmd == "findFileGoto")
  385. {
  386. // 下拉框转跳资源管理器
  387. this.searchCmd(info.cmd)
  388. }else if(info.cmd == "findJsFileAndOpen")
  389. {
  390. // 下拉框转跳资源管理器
  391. this.searchCmd(info.cmd)
  392. }
  393. },
  394. 'scene:saved'(){
  395. // Editor.log("事件 save")
  396. }
  397. },
  398. };