index.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * @Author: CGT (caogtaa@gmail.com)
  3. * @Date: 2020-01-16 22:09:21
  4. * @Last Modified by: CGT (caogtaa@gmail.com)
  5. * @Last Modified time: 2020-01-16 23:45:39
  6. */
  7. 'use strict';
  8. let _config = {};
  9. Editor.Panel.extend({
  10. // css style for panel
  11. style: `
  12. :host { margin: 5px; }
  13. :host ui-input { width: 100%; }
  14. h2 { color: #f90; }
  15. ul {
  16. list-style-type: none;
  17. padding-inline-start: 20px;
  18. }
  19. ul.root {
  20. padding-inline-start: 15px;
  21. }
  22. ul li {
  23. padding: 2px 10px 1px 0;
  24. text-align: -webkit-match-parent;
  25. color: #ccc;
  26. border-bottom: 1px solid #454545;
  27. box-sizing: border-box;
  28. }
  29. span.selected {
  30. background: #555;
  31. }
  32. .caret {
  33. cursor: pointer;
  34. user-select: none;
  35. width: 12px;
  36. display: inline-block;
  37. }
  38. .caret-holder {
  39. width: 12px;
  40. display: inline-block;
  41. }
  42. .caret::before {
  43. content: "\\25B6";
  44. color: #ccc;
  45. display: inline-block;
  46. margin-right: 6px;
  47. }
  48. .caret-down::before {
  49. transform: rotate(90deg);
  50. }
  51. .nested {
  52. display: none;
  53. }
  54. .collapsed {
  55. }
  56. .active:not(.collapsed) {
  57. display: block;
  58. }
  59. `,
  60. // html template for panel
  61. template: `
  62. <h2>Custom Scene Context Menu</h2>
  63. <hr />
  64. <p>Click to edit item, right click to add / remove item</p>
  65. <div v-if="d.loaded">
  66. <ul class="root">
  67. <li>
  68. <span class="caret caret-down" v-on:click="toggleCaret"></span>
  69. <span v-bind:class="{ selected: d.focus_item==null }" v-on:click="d.focus_item=null;" v-on:contextmenu="onContextMenu($event, true, null)">Context Menu (root)</span>
  70. <ul class="nested active">
  71. <li v-for="c in d.config">
  72. <span v-show="c.type == '2'" class="caret" v-on:click="toggleCaret"></span>
  73. <span v-show="c.type != '2'" class="caret-holder">&nbsp;</span>
  74. <span v-bind:class="{ selected: d.focus_item==c }" v-on:click="d.focus_item=c;" v-on:contextmenu="onContextMenu($event, false, c)">{{c.name}}</span>
  75. <ul class="nested" v-bind:class="{ collapsed: c.type!='2' }">
  76. <li v-for="subc in c.submenu">
  77. <span class="caret-holder">&nbsp;</span>
  78. <span v-bind:class="{ selected: d.focus_item==subc }" v-on:click="d.focus_item=subc;" v-on:contextmenu="onContextMenu($event, false, subc, c)">{{subc.name}}</span>
  79. </li>
  80. </ul>
  81. </li>
  82. </ul>
  83. </li>
  84. </ul>
  85. <ui-box-container v-if="d.focus_item">
  86. <ui-prop name="Name">
  87. <ui-input v-value="d.focus_item.name" placeholder="menu display name"></ui-input>
  88. </ui-prop>
  89. <ui-prop name="Type">
  90. <ui-select v-value="d.focus_item.type" @change="onTypeChange($event)">
  91. <option value="0">Prefab</option>
  92. <option value="1">Command</option>
  93. <option value="2">Sub Menu</option>
  94. </ui-select>
  95. </ui-prop>
  96. <ui-prop name="Prefab uuid" v-if="d.focus_item.type=='0'">
  97. <ui-input v-value="d.focus_item.uuid" placeholder="prefab uuid"></ui-input>
  98. </ui-prop>
  99. <ui-prop name="Command" v-if="d.focus_item.type=='1'">
  100. <ui-input v-value="d.focus_item.command" placeholder="PluginName:CommandName"></ui-input>
  101. </ui-prop>
  102. <ui-prop name="Parameter" v-if="d.focus_item.type=='1'">
  103. <ui-input v-value="d.focus_item.param" placeholder="(Optional) Command Parameter"></ui-input>
  104. </ui-prop>
  105. </ui-box-container>
  106. <hr />
  107. <ui-button id="save" @confirm="onSaveConfirm">Save Menu</ui-button>
  108. </div>
  109. `,
  110. // element and variable binding
  111. $: {
  112. },
  113. // method executed when template and styles are successfully loaded and initialized
  114. ready () {
  115. const fs = require('fs');
  116. let configPath = Editor.Project.path + '/scene-menu-config.json';
  117. Editor.log(configPath);
  118. if (!fs.existsSync(configPath)) {
  119. // read default config
  120. configPath = Editor.url('packages://cc-ext-scene-menu/default-config.json');
  121. }
  122. let saveConfig = () => {
  123. let data = JSON.stringify(_config, null, 4);
  124. fs.writeFile(configPath, data, function(err) {
  125. if (err) {
  126. Editor.log(err);
  127. return;
  128. }
  129. Editor.Ipc.sendToMain('cc-ext-scene-menu:update-context-menu');
  130. });
  131. };
  132. let initWindow = (config) => {
  133. _config = config;
  134. new window.Vue({
  135. el: this.shadowRoot,
  136. data: {
  137. d: {
  138. config: _config,
  139. focus_item: null,
  140. loaded: true,
  141. command: '',
  142. param: ''
  143. }
  144. },
  145. methods: {
  146. onSaveConfirm (e) {
  147. e.stopPropagation();
  148. saveConfig();
  149. },
  150. toggleCaret (e) {
  151. e.target.classList.toggle('caret-down');
  152. e.target.parentElement.querySelector(".nested").classList.toggle("active");
  153. },
  154. onContextMenu (e, isRoot, obj = null, parent = null) {
  155. this.d.focus_item = obj;
  156. let d = this.d;
  157. let electron = require("electron");
  158. let menuTemplate = [];
  159. let currentTarget = e.currentTarget;
  160. if (!parent) {
  161. // max depth is 2
  162. menuTemplate.push({
  163. label: "Add Child",
  164. click () {
  165. let newItem = {
  166. type: "0",
  167. name: "New Item",
  168. uuid: "",
  169. submenu: [],
  170. command: "",
  171. param: ""
  172. };
  173. if (isRoot) {
  174. d.config.push(newItem);
  175. } else {
  176. if (!obj.submenu) {
  177. obj.submenu = [];
  178. }
  179. obj.type = 2;
  180. obj.submenu.push(newItem);
  181. currentTarget.parentElement.querySelector(".nested").classList.add("active");
  182. currentTarget.parentElement.querySelector(".nested").classList.remove("collapsed");
  183. currentTarget.parentElement.querySelector(".caret").classList.add("caret-down");
  184. }
  185. }
  186. });
  187. }
  188. if (!isRoot) {
  189. let d = this.d;
  190. menuTemplate.push({
  191. label: "Delete",
  192. click () {
  193. if (obj && parent) {
  194. // deep level item
  195. const index = parent.submenu.indexOf(obj);
  196. if (index > -1) {
  197. if (d.focus_item == obj) {
  198. d.focus_item = null;
  199. }
  200. parent.submenu.splice(index, 1);
  201. }
  202. } else if (obj) {
  203. // 1st level item
  204. const index = config.indexOf(obj);
  205. if (index > -1) {
  206. if (d.focus_item == obj) {
  207. d.focus_item = null;
  208. }
  209. config.splice(index, 1);
  210. }
  211. }
  212. }
  213. });
  214. }
  215. let menu = electron.remote.Menu.buildFromTemplate(menuTemplate);
  216. menu.popup();
  217. e.preventDefault();
  218. },
  219. onTypeChange(e) {
  220. }
  221. }
  222. });
  223. };
  224. fs.readFile(configPath, function(err, data) {
  225. if (err) {
  226. // file not exists
  227. initWindow([]);
  228. return;
  229. }
  230. let config = [];
  231. try {
  232. config = JSON.parse(data);
  233. // Editor.log(`index.js read data: ${data}`);
  234. } catch (err) {
  235. } finally {
  236. initWindow(config);
  237. }
  238. });
  239. },
  240. // register your ipc messages here
  241. messages: {
  242. 'cc-ext-scene-menu:demo' (event) {
  243. Editor.log('this is Demo');
  244. }
  245. }
  246. });