ContextMenu.hx 3.8 KB

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