ContextMenu.hx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package hide.comp;
  2. typedef ContextMenuItem = {
  3. var label : String;
  4. @:optional var menu : Array<ContextMenuItem>;
  5. @:optional var click : Void -> Void;
  6. @:optional var enabled : Bool;
  7. @:optional var checked : Bool;
  8. @:optional var isSeparator : Bool;
  9. @:optional var icon : String;
  10. @:optional var stayOpen : Bool;
  11. @:optional var keys : String;
  12. }
  13. class ContextMenu {
  14. static var MENUS : Array<nw.Menu>;
  15. static var CTX_ANCHOR : Element;
  16. static final CONTEXTMENU_LAYER = 900;
  17. public function new( config : Array<ContextMenuItem> ) {
  18. var ide = hide.Ide.inst;
  19. var args = {
  20. selector: '#ctx-menu-anchor',
  21. trigger: "none",
  22. items: makeMenu(config),
  23. position: (opt, x, y) -> {
  24. opt.$menu.css({ left: ide.mouseX, top: ide.mouseY });
  25. },
  26. zIndex: CONTEXTMENU_LAYER + 2,
  27. useModal: false,
  28. scrollable: true,
  29. }
  30. // wait until mousedown to get correct mouse pos
  31. haxe.Timer.delay(function() {
  32. if( CTX_ANCHOR == null ) {
  33. CTX_ANCHOR = new Element('<div id="ctx-menu-anchor">');
  34. new Element("body").append(CTX_ANCHOR);
  35. }
  36. untyped jQuery.contextMenu('destroy', '#ctx-menu-anchor');
  37. untyped jQuery.contextMenu(args);
  38. (CTX_ANCHOR : Dynamic).contextMenu();
  39. }, 0);
  40. // Old version that uses nwjs context menu
  41. // MENUS = [];
  42. // var menu = makeNwMenu(config);
  43. // // wait until mousedown to get correct mouse pos
  44. // haxe.Timer.delay(function() {
  45. // if( MENUS[0] == menu )
  46. // menu.popup(ide.mouseX, ide.mouseY);
  47. // }, 0);
  48. }
  49. public static function hideMenu() {
  50. if( CTX_ANCHOR != null )
  51. (CTX_ANCHOR : Dynamic).contextMenu("hide");
  52. }
  53. function makeMenu( config : Array<ContextMenuItem> ) {
  54. var ret : Dynamic = {};
  55. for( i in 0...config.length ) {
  56. Reflect.setField(ret, "" + i, makeMenuItem(config[i]));
  57. }
  58. return ret;
  59. }
  60. function makeMenuItem( i:ContextMenuItem ) : Dynamic {
  61. if( i.isSeparator) {
  62. return {
  63. type: "cm_separator",
  64. };
  65. }
  66. var name = "";
  67. if( i.icon != null && i.checked == null)
  68. name += '<span class="context-icon"><span class="ico ico-${i.icon}"></span></span>';
  69. name += i.label;
  70. if( i.keys != null ) {
  71. name += '<span class="contextmenu-keys">' + toKeyString(i.keys) + "</span>";
  72. }
  73. var autoclose = (i.stayOpen == null) ? true : !i.stayOpen;
  74. var emptySubMenu = i.menu != null && i.menu.length <= 0;
  75. var ret : Dynamic = {
  76. name : name,
  77. isHtmlName : true,
  78. callback : function(itemKey, opt, rootMenu, originalEvent) {
  79. i.click();
  80. return autoclose;
  81. },
  82. disabled : (i.enabled == null ? false : !i.enabled) || emptySubMenu,
  83. items : (i.menu == null || emptySubMenu) ? null : makeMenu(i.menu),
  84. };
  85. if( i.checked != null ) {
  86. ret.type = 'checkbox';
  87. ret.selected = i.checked;
  88. ret.events = {
  89. change : function(event) {
  90. if( autoclose )
  91. hideMenu();
  92. i.click();
  93. },
  94. };
  95. }
  96. return ret;
  97. }
  98. function toKeyString( keyCode : String ) {
  99. return keyCode.split("-").join("+");
  100. }
  101. function toNwKeys( keyCode : String ) {
  102. if( keyCode == null )
  103. return null;
  104. var splitKeys = keyCode.split("-");
  105. return {
  106. key: splitKeys[splitKeys.length - 1],
  107. modifiers: [for( i in 0...(splitKeys.length - 1)) splitKeys[i]].join("+"),
  108. };
  109. }
  110. function makeNwMenu( config : Array<ContextMenuItem> ) {
  111. var m = new nw.Menu({type:ContextMenu});
  112. MENUS.push(m);
  113. for( i in config )
  114. m.append(makeNwMenuItem(i));
  115. return m;
  116. }
  117. function makeNwMenuItem(i:ContextMenuItem) {
  118. var mconf : nw.MenuItem.MenuItemOptions = { label : i.label, type : i.checked != null ? Checkbox : i.isSeparator ? Separator : Normal };
  119. var keys = toNwKeys(i.keys);
  120. if( keys != null ) {
  121. mconf.key = keys.key;
  122. mconf.modifiers = keys.modifiers;
  123. }
  124. if( i.menu != null ) mconf.submenu = makeNwMenu(i.menu);
  125. var m = new nw.MenuItem(mconf);
  126. if( i.checked != null ) m.checked = i.checked;
  127. if( i.enabled != null ) m.enabled = i.enabled;
  128. m.click = function() {
  129. try {
  130. i.click();
  131. } catch( e : Dynamic ) {
  132. hide.Ide.inst.error(e);
  133. }
  134. }
  135. return m;
  136. }
  137. }