diff --git a/src/command/Menus.js b/src/command/Menus.js index f9a1e24a56c..85f9c6c69cb 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -66,21 +66,28 @@ define(function (require, exports, module) { * looser coupling to Bracket's internal MenuItems and makes menu organization * more semantic. * Use these constants as the "relativeID" parameter when calling addMenuItem() and - * specify a position of FIRST or LAST. + * specify a position of FIRST_IN_SECTION or LAST_IN_SECTION. + * + * Menu sections are denoted by dividers or the beginning/end of a menu */ var MenuSection = { - FILE_OPEN_CLOSE_MENU: "file-open-close-menu-section", - FILE_SAVE_MENU: "file-save-menu-section", - FILE_LIVE_MENU: "file-live-menu-section", + // Menu Section Command ID to mark the section + FILE_OPEN_CLOSE_COMMANDS: {sectionMarker: Commands.FILE_NEW}, + FILE_SAVE_COMMANDS: {sectionMarker: Commands.FILE_SAVE}, + FILE_LIVE: {sectionMarker: Commands.FILE_LIVE_FILE_PREVIEW}, + + EDIT_SELECTION_COMMANDS: {sectionMarker: Commands.EDIT_SELECT_ALL}, + EDIT_FIND: {sectionMarker: Commands.EDIT_FIND}, + EDIT_REPLACE_COMMANDS: {sectionMarker: Commands.EDIT_REPLACE}, + EDIT_MODIFY_SELECTION: {sectionMarker: Commands.EDIT_INDENT}, - EDIT_MODIFY_SELECTION_MENU: "edit-modify-selection-menu-section", - EDIT_FIND_MENU: "find-menu-section", - EDIT_REPLACE_MENU: "replace-menu-section", - EDIT_SELECTED_TEXT_COMMANDS: "selected-text-commands-menu-group", + VIEW_HIDESHOW_COMMANDS: {sectionMarker: Commands.VIEW_HIDE_SIDEBAR}, + VIEW_FONTSIZE_COMMANDS: {sectionMarker: Commands.VIEW_INCREASE_FONT_SIZE}, - NAVIGATE_GOTO_MENU: "goto-menu-section", - NAVIGATE_QUICK_EDIT_MENU: "quick-edit-menu-section" + NAVIGATE_GOTO_COMMANDS: {sectionMarker: Commands.NAVIGATE_QUICK_OPEN}, + NAVIGATE_QUICK_EDIT_COMMANDS: {sectionMarker: Commands.TOGGLE_QUICK_EDIT} }; + /** * Insertion position constants @@ -88,10 +95,12 @@ define(function (require, exports, module) { * specify the relative position of a newly created menu object * @enum {string} */ - var BEFORE = "before"; - var AFTER = "after"; - var FIRST = "first"; - var LAST = "last"; + var BEFORE = "before"; + var AFTER = "after"; + var FIRST = "first"; + var LAST = "last"; + var FIRST_IN_SECTION = "firstInSection"; + var LAST_IN_SECTION = "lastInSection"; /** * Other constants @@ -196,6 +205,15 @@ define(function (require, exports, module) { // Determine where to insert. Default is LAST. var inserted = false; if (position) { + + // Adjust relative position for menu section positions since $relativeElement + // has already been resolved by _getRelativeMenuItem() to a menuItem + if (position === FIRST_IN_SECTION) { + position = BEFORE; + } else if (position === LAST_IN_SECTION) { + position = AFTER; + } + if (position === FIRST) { $list.prepend($element); inserted = true; @@ -275,12 +293,27 @@ define(function (require, exports, module) { this.id = id; } + Menu.prototype._getMenuItemForCommand = function (command) { + var foundMenuItem, key, menuItem; + for (key in menuItemMap) { + if (menuItemMap.hasOwnProperty(key)) { + menuItem = menuItemMap[key]; + if (menuItem.getCommand() === command) { + foundMenuItem = menuItem; + break; + } + } + } + return $(_getHTMLMenuItem(foundMenuItem.id)).closest("li"); + }; + /** * Determine relative MenuItem * - * @param {?string} relativeID - id of command (future: also sub-menu, or menu section). + * @param {?string} relativeID - id of command (future: sub-menu). + * @param {?string} position - only needed when relativeID is a MenuSection */ - Menu.prototype._getRelativeMenuItem = function (relativeID) { + Menu.prototype._getRelativeMenuItem = function (relativeID, position) { var $relativeElement, key, menuItem, @@ -288,25 +321,40 @@ define(function (require, exports, module) { foundMenuItem; if (relativeID) { - // Lookup Command for this Command id - var command = CommandManager.get(relativeID); - - if (command) { - // Find MenuItem that has this command - for (key in menuItemMap) { - if (menuItemMap.hasOwnProperty(key)) { - menuItem = menuItemMap[key]; - if (menuItem.getCommand() === command) { - foundMenuItem = menuItem; - break; - } + if (position === FIRST_IN_SECTION || position === LAST_IN_SECTION) { + if (!relativeID.hasOwnProperty("sectionMarker")) { + console.log("Bad Parameter in _getRelativeMenuItem(): relativeID must be a MenuSection when position refers to a menu section"); + } + + // Determine the $relativeElement by traversing the sibling list and + // stop at the first divider found + var $sectionMarker = this._getMenuItemForCommand(CommandManager.get(relativeID.sectionMarker)); + var $sectionItems = $sectionMarker.siblings(); + var $listElem = $sectionMarker; + $relativeElement = $listElem; + while (true) { + $listElem = (position === FIRST_IN_SECTION ? $listElem.prev() : $listElem.next()); + if ($listElem.length === 0) { + break; + } else if ($listElem.find(".divider").length > 0) { + break; + } else { + $relativeElement = $listElem; } } - - if (foundMenuItem) { - $relativeElement = $(_getHTMLMenuItem(foundMenuItem.id)).closest("li"); + } else { + // handle FIRST, LAST, BEFORE, & AFTER + var command = CommandManager.get(relativeID); + if (command) { + // Lookup Command for this Command id + // Find MenuItem that has this command + $relativeElement = this._getMenuItemForCommand(command); } } + + return $relativeElement; + } else { + console.log("Bad Parameter in _getRelativeMenuItem(): relativeID not specified"); } return $relativeElement; @@ -331,9 +379,9 @@ define(function (require, exports, module) { * one or more key bindings to associate with the supplied command. * @param {?string} position - constant defining the position of new the MenuItem relative * to other MenuItems. Default is LAST. (see Insertion position constants). - * @param {?string} relativeID - id of command (future: also sub-menu, or menu section) that - * the new menuItem will be positioned relative to. Required when position is - * AFTER or BEFORE, ignored when position is FIRST or LAST + * @param {?string} relativeID - id of command or menu section (future: sub-menu) that + * the new menuItem will be positioned relative to. Required for all position constants + * except FIRST and LAST. * * @return {MenuItem} the newly created MenuItem */ @@ -390,7 +438,7 @@ define(function (require, exports, module) { } // Insert menu item - var $relativeElement = this._getRelativeMenuItem(relativeID); + var $relativeElement = this._getRelativeMenuItem(relativeID, position); _insertInList($("li#" + StringUtils.jQueryIdEscape(this.id) + " > ul.dropdown-menu"), $menuItem, position, $relativeElement); @@ -422,8 +470,8 @@ define(function (require, exports, module) { * @param {?string} position - constant defining the position of new the divider relative * to other MenuItems. Default is LAST. (see Insertion position constants). * @param {?string} relativeID - id of menuItem, sub-menu, or menu section that the new - * divider will be positioned relative to. Required when position is - * AFTER or BEFORE, ignored when position is FIRST or LAST. + * divider will be positioned relative to. Required for all position constants + * except FIRST and LAST * * @return {MenuItem} the newly created divider */ @@ -940,6 +988,8 @@ define(function (require, exports, module) { exports.AFTER = AFTER; exports.LAST = LAST; exports.FIRST = FIRST; + exports.FIRST_IN_SECTION = FIRST_IN_SECTION; + exports.LAST_IN_SECTION = LAST_IN_SECTION; exports.DIVIDER = DIVIDER; exports.getMenu = getMenu; exports.getMenuItem = getMenuItem; diff --git a/test/spec/Menu-test.js b/test/spec/Menu-test.js index 9a4a35bc851..4e723182802 100644 --- a/test/spec/Menu-test.js +++ b/test/spec/Menu-test.js @@ -143,6 +143,53 @@ define(function (require, exports, module) { }); }); + it("it should add menu to beginnging and end of menu section", function () { + // set up test menu and menu items + CommandManager.register("Brackets Test Command Custom 0", "custom.command0", function () {}); + CommandManager.register("Brackets Test Command Custom 1", "custom.command1", function () {}); + CommandManager.register("Brackets Test Command Custom 2", "custom.command2", function () {}); + CommandManager.register("Brackets Test Command Custom 3", "custom.command3", function () {}); + CommandManager.register("Brackets Test Command Custom 4", "custom.command4", function () {}); + CommandManager.register("Brackets Test Command Custom 5", "custom.command5", function () {}); + CommandManager.register("Brackets Test Command Custom 6", "custom.command6", function () {}); + CommandManager.register("Brackets Test Command Custom 7", "custom.command7", function () {}); + CommandManager.register("Brackets Test Command Custom 8", "custom.command8", function () {}); + var menu = Menus.addMenu("Custom", "menu-custom"); + menu.addMenuItem("custom.command0"); + menu.addMenuItem("custom.command1"); + menu.addMenuDivider(); + menu.addMenuItem("custom.command2"); + menu.addMenuItem("custom.command3"); + + // create mock menu sections + var menuSectionCmd0 = {sectionMarker: "custom.command0"}, + menuSectionCmd2 = {sectionMarker: "custom.command2"}; + + var listSelector = "#menu-custom > ul"; + + // Add new menu to END of menuSectionCmd0 + var menuItem = menu.addMenuItem("custom.command4", null, Menus.LAST_IN_SECTION, menuSectionCmd0); + var $listItems = testWindow.$(listSelector).children(); + expect($listItems.length).toBe(6); + expect($($listItems[2]).find("a#menu-custom-custom\\.command4").length).toBe(1); + + // Add new menu to END of menuSectionCmd2 + menuItem = menu.addMenuItem("custom.command5", null, Menus.LAST_IN_SECTION, menuSectionCmd2); + $listItems = testWindow.$(listSelector).children(); + expect($($listItems[6]).find("a#menu-custom-custom\\.command5").length).toBe(1); + + // Add new menu to BEGINNING of menuSectionCmd0 + menuItem = menu.addMenuItem("custom.command6", null, Menus.FIRST_IN_SECTION, menuSectionCmd0); + $listItems = testWindow.$(listSelector).children(); + expect($($listItems[0]).find("a#menu-custom-custom\\.command6").length).toBe(1); + + // Add new menu to BEGINNING of menuSectionCmd2 + menuItem = menu.addMenuItem("custom.command7", null, Menus.FIRST_IN_SECTION, menuSectionCmd2); + $listItems = testWindow.$(listSelector).children(); + expect($($listItems[0]).find("a#menu-custom-custom\\.command6").length).toBe(1); + + }); + it("should not add duplicate menu", function () { runs(function () { var $listItems = testWindow.$("#main-toolbar > ul.nav").children();