From 5daac882349facd272b09fd44e59997f3be69b96 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Thu, 7 Jun 2012 09:09:32 -0700 Subject: [PATCH 01/35] context menu prototype --- src/command/Menus.js | 113 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 2e684a7dfbb..5cc66fe9ea7 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -23,7 +23,7 @@ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, $, brackets */ +/*global define, $, brackets, window */ define(function (require, exports, module) { 'use strict'; @@ -48,6 +48,8 @@ define(function (require, exports, module) { }; + + /** * Brackets Application Menu Section Constants * It is preferred that plug-ins specify the location of new MenuItems @@ -93,6 +95,12 @@ define(function (require, exports, module) { */ var menuMap = {}; + /** + * Maps contextMenuID's to ContextMenu objects + * @type {Object.} + */ + var contextMenuMap = {}; + /** * Maps menuItemID's to MenuItem objects * @type {Object.} @@ -108,6 +116,15 @@ define(function (require, exports, module) { return menuMap[id]; } + /** + * Retrieves the ContextMenu object for the corresponding id. + * @param {string} id + * @return {ContextMenu} + */ + function getContextMenu(id) { + return contextMenuMap[id]; + } + /** * Retrieves the MenuItem object for the corresponding id. * @param {string} id @@ -190,6 +207,8 @@ define(function (require, exports, module) { } } + + /** * @constructor * @private @@ -321,7 +340,7 @@ define(function (require, exports, module) { // Insert menu item var $relativeElement = relativeID && $(_getHTMLMenuItem(relativeID)).closest("li"); - _insertInList($("#main-toolbar li#" + this.id + " > ul.dropdown-menu"), $menuItem, position, $relativeElement); + _insertInList($("li#" + this.id + " > ul.dropdown-menu"), $menuItem, position, $relativeElement); // Initialize MenuItem state if (!menuItem.isDivider) { @@ -524,6 +543,63 @@ define(function (require, exports, module) { return menu; } + + function ContextMenu(id) { + //TODO: check id + this.id = id; + this.menu = new Menu(id); + + var $newMenu = $(""); + + var $toggle = $("") + .hide(); + + $newMenu.append($toggle) + .append("") + .css({"position": "absolute", + "z-index": "1000", + "list-style-type": "none", + "top": 200, + "left": 300}); + + $(window.document.body).append($newMenu); + } + + ContextMenu.prototype.getMenu = function () { + return this.menu; + }; + + ContextMenu.prototype.open = function (x, y) { + // TODO: positioning logic + + $.each(contextMenuMap, function (index, cmenu) { + cmenu.close(); + }); + + $("#" + this.id) + .addClass("open") + .css({"left": x, + "top": y - 30}); // todo: compute offset + }; + + ContextMenu.prototype.close = function () { + $("#" + this.id).removeClass("open"); + }; + + function addContextMenu(id) { + if (!id) { + throw new Error("call to addContextMenu() is missing required parameters"); + } + + // Guard against duplicate menu ids + if (contextMenuMap[id]) { + throw new Error("Context Menu added with same name and id of existing Context Menu: " + id); + } + + var cmenu = new ContextMenu(id); + contextMenuMap[id] = cmenu; + return cmenu; + } /** NOT IMPLEMENTED * Removes Menu @@ -623,6 +699,39 @@ define(function (require, exports, module) { menu.addMenuItem("menu-debug-close-browser", Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS); menu.addMenuItem("menu-debug-use-tab-chars", Commands.DEBUG_USE_TAB_CHARS); + + + /** + * Context Menus Test Code + * + */ + var project_cmenu = addContextMenu("cmenutest1"); + menu = project_cmenu.getMenu(); + menu.addMenuItem("test4", Commands.FILE_OPEN); + menu.addMenuItem("test5", Commands.FILE_CLOSE); + menu.addMenuItem("test6", Commands.FILE_NEW); + + var editor_cmenu = addContextMenu("cmenutest2"); + menu = editor_cmenu.getMenu(); + menu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); + menu.addMenuItem("test2", Commands.EDIT_DUPLICATE); + menu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); + + + + $("#projects").mousedown(function (e) { + if (e.which === 3) { + project_cmenu.open(e.pageX, e.pageY); + } + }); + + $("#editor-holder").mousedown(function (e) { + if (e.which === 3) { + editor_cmenu.open(e.pageX, e.pageY); + } + }); + + $("#main-toolbar .dropdown") // Prevent clicks on the top-level menu bar from taking focus // Note, bootstrap handles this already for the menu drop downs From 24d03a89c26c98f1130643b47e82a360cf5205d0 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Thu, 7 Jun 2012 13:54:19 -0700 Subject: [PATCH 02/35] hack less to make context menus look right --- src/styles/brackets_patterns_override.less | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 3ed27a3d40c..8a38634d1c8 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -157,7 +157,110 @@ } } } + + // HACK: TODO: factor commonalities out of menuAppearance and .toolbar .nav + .menuAppearance { + @menubar-top-padding: 8px; + @menubar-bottom-padding: 6px; + @menubar-h-padding: 9px; + @item-highlight-color: #e0f0fa; + // + .menu-shortcut { + float: right; + } + + // The 1px adjustments here are to account for the border around the top-level menu items. + margin: (@toolbar-top-gap-px - @menubar-top-padding - 1) 0 (-@menubar-bottom-padding + 1) (-@menubar-h-padding + 4); + + // General item appearance + a { + color: @bc-black; + text-shadow: none; + } + + // Menubar item appearance + .dropdown-toggle { + padding: @menubar-top-padding @menubar-h-padding @menubar-bottom-padding; + border: 1px solid rgba(0, 0, 0, 0); // transparent border just to hold the layout so it doesn't shift on hover + color: fadeout(@bc-black, 25%); + font-weight: @font-weight-light; + } + .dropdown-toggle:hover, .dropdown.open .dropdown-toggle { + /* Note: we need several selectors for this in order to match the specificity of all the various Bootstrap + color rules we need to override (and Bootstrap has several selectors because the colors really differ + in their defaults). */ + color: @bc-black; + background: @item-highlight-color; + border-color: #dbeaf4; + } + + // no triangle icon after menu items + .dropdown-toggle:after { + border: 0; + margin: 0; + } + + // Menu dropdown appearance + .dropdown-menu { + top: 34px; // Offset from top of menubar item + + background-color: @bc-white; + border: 1px solid #c8c8c8; + .border-radius(0 0 3px 3px); + .box-shadow(0 6px 18px 0 rgba(0, 0, 0, .2)); + + // Menu items + li { + a { + font-size: @menu-item-font-size; + line-height: 18px; + + // Slightly less padding on left to account for checkmark. + // More padding on top than bottom to center font within its bg highlight better. + padding: 2px 10px 0px 6px; + + color: @bc-black; + text-shadow: none; + white-space: nowrap; + .box-shadow(none); + + &:hover { + color: @bc-black; + background: @item-highlight-color; + } + + &.disabled { + color: @bc-gray; + + &:hover { + background: @bc-white; + } + } + + /* hidden checkmark for all list item content ensures consistent left spacing */ + &::before { + content: "✓\00a0"; /* non-breaking space */ + visibility: hidden; + } + /* toggle checkmark visibility */ + &.checked::before { + visibility: visible; + } + } + } + + .divider { + background-color: #eaeaea; + border: 0; + } + } + + // Ensure a minimum amount of space to the left of the shortcut + .menu-shortcut { + margin-left: 15px; + } + } /* Menu-related styles */ .toolbar .nav { From 95aa501212ec3bef106f66af00af72e8d4059229 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Thu, 7 Jun 2012 13:54:36 -0700 Subject: [PATCH 03/35] Completing first draft of API --- src/command/Menus.js | 70 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 5cc66fe9ea7..ef5295ea4da 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -544,16 +544,36 @@ define(function (require, exports, module) { return menu; } + /** + * @constructor + * @private + * + * Represents a context menu that can open at a specific location in the UI. Call getMenu() + * to access the Menu object for adding MenuItems. + * + * Clients should nowtcreate this object directly and should instead use registerContextMenu() + * to create new ContextMenu objects. + * + * Context menus in brackets may be HTML-based on native so clients should not reach into + * the HTML and should instead manipulate ContextMenus through the API. + * + * Events: + * beforeContextMenuOpen + * contextMenuClose + * + */ function ContextMenu(id) { - //TODO: check id this.id = id; this.menu = new Menu(id); - var $newMenu = $(""); + // TODO: menuAppearance is a temporary class to make the context menus look correct + // until the CSS code for menus is refactored to support menus and context menus + var $newMenu = $(""); var $toggle = $("") .hide(); + // TODO: move this css into a less file for context menus $newMenu.append($toggle) .append("") .css({"position": "absolute", @@ -565,30 +585,61 @@ define(function (require, exports, module) { $(window.document.body).append($newMenu); } + /** + * Returns the menu assocatiated with this ContextMenu + * @return {Menu} + */ ContextMenu.prototype.getMenu = function () { return this.menu; }; + /** + * Displays the ContextMenu at the specified location and dispatches the + * "contextMenuOpen" event. All other menus and ContextMenus will be closed + * bofore a new menu is shown. + * + * @param {number} x - page relative x coodinate + * @param {number} y - page relative y coodinate + */ ContextMenu.prototype.open = function (x, y) { // TODO: positioning logic - $.each(contextMenuMap, function (index, cmenu) { - cmenu.close(); - }); + $(".dropdown").removeClass("open"); $("#" + this.id) .addClass("open") .css({"left": x, "top": y - 30}); // todo: compute offset + + $(this).triggerHandler("beforeContextMenuOpen"); }; + /** + * Closes the context menu and dispatches the "contextMenuOpen" event. + */ ContextMenu.prototype.close = function () { $("#" + this.id).removeClass("open"); + + $(this).triggerHandler("contextMenuClose"); }; - function addContextMenu(id) { + /** + * Registers new context menu with Brackets. + * After registering a context menu clients should: + * 1) call yourContextMenu.getMenu() to access to the Menu object + * 2) use addMenuItem() to add items to the Menu object + * 3) call open() to show the context menu (often trigged via an event handler) + * + * To make menu items be contextual to things like selection listen for the "beforeContextMenuOpen" + * to make changes to Command objects before the context menu is shown. MenuItems are views of + * Commands and control a MenuItem's name, enabled state, and checked stae. + * + * @param {string} id + * @return {ContextMenu} the newly created context menu + */ + function registerContextMenu(id) { if (!id) { - throw new Error("call to addContextMenu() is missing required parameters"); + throw new Error("call to registerContextMenu() is missing required parameters"); } // Guard against duplicate menu ids @@ -705,14 +756,15 @@ define(function (require, exports, module) { * Context Menus Test Code * */ - var project_cmenu = addContextMenu("cmenutest1"); + var project_cmenu = registerContextMenu("cmenutest1"); menu = project_cmenu.getMenu(); menu.addMenuItem("test4", Commands.FILE_OPEN); menu.addMenuItem("test5", Commands.FILE_CLOSE); menu.addMenuItem("test6", Commands.FILE_NEW); - var editor_cmenu = addContextMenu("cmenutest2"); + var editor_cmenu = registerContextMenu("cmenutest2"); menu = editor_cmenu.getMenu(); + menu.addMenuItem("test0", Commands.SHOW_INLINE_EDITOR); menu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); menu.addMenuItem("test2", Commands.EDIT_DUPLICATE); menu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); From 37bc9ff4c66d2dfc22ebaa5f92a23c7db9e6f754 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Fri, 8 Jun 2012 10:55:42 -0700 Subject: [PATCH 04/35] api work --- src/command/Menus.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/command/Menus.js b/src/command/Menus.js index ef5295ea4da..13be52421eb 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -816,4 +816,6 @@ define(function (require, exports, module) { exports.addMenu = addMenu; exports.Menu = Menu; exports.MenuItem = MenuItem; + exports.registerContextMenu = registerContextMenu; + exports.ContextMenu = ContextMenu; }); From 203725617ede3491e0b41dc63706da24fd37609f Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Fri, 8 Jun 2012 15:09:30 -0700 Subject: [PATCH 05/35] add missing @extends annotation --- src/editor/InlineTextEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editor/InlineTextEditor.js b/src/editor/InlineTextEditor.js index 0658806a6b6..a1bedf54fce 100644 --- a/src/editor/InlineTextEditor.js +++ b/src/editor/InlineTextEditor.js @@ -73,7 +73,7 @@ define(function (require, exports, module) { /** * @constructor - * + * @extends {InlineWidget} */ function InlineTextEditor() { InlineWidget.call(this); From 7c431a92d22cd990965c43b101768956f79ee017 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Fri, 8 Jun 2012 15:09:44 -0700 Subject: [PATCH 06/35] code review fixes for context menu API --- src/command/Menus.js | 80 ++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 13be52421eb..66ed20c1760 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -23,7 +23,7 @@ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, $, brackets, window */ +/*global define, $, brackets, window, MouseEvent */ define(function (require, exports, module) { 'use strict'; @@ -546,15 +546,15 @@ define(function (require, exports, module) { /** * @constructor + * @extends {Menu} * @private * - * Represents a context menu that can open at a specific location in the UI. Call getMenu() - * to access the Menu object for adding MenuItems. + * Represents a context menu that can open at a specific location in the UI. * - * Clients should nowtcreate this object directly and should instead use registerContextMenu() + * Clients should not create this object directly and should instead use registerContextMenu() * to create new ContextMenu objects. * - * Context menus in brackets may be HTML-based on native so clients should not reach into + * Context menus in brackets may be HTML-based or native so clients should not reach into * the HTML and should instead manipulate ContextMenus through the API. * * Events: @@ -584,32 +584,43 @@ define(function (require, exports, module) { $(window.document.body).append($newMenu); } + ContextMenu.prototype = new Menu(); + ContextMenu.prototype.constructor = ContextMenu; + ContextMenu.prototype.parentClass = Menu.prototype; - /** - * Returns the menu assocatiated with this ContextMenu - * @return {Menu} - */ - ContextMenu.prototype.getMenu = function () { - return this.menu; - }; /** * Displays the ContextMenu at the specified location and dispatches the - * "contextMenuOpen" event. All other menus and ContextMenus will be closed + * "contextMenuOpen" event.The menu location may be adjusted to prevent + * clipping by the browser window. All other menus and ContextMenus will be closed * bofore a new menu is shown. + * + * @param {MouseEvent | {pageX:number, pageY:number}} mouseOrLocation - pass a MouseEvent + * to display the menu near the mouse or pass in an object with page x/y coordinates + * for a specific location. * - * @param {number} x - page relative x coodinate - * @param {number} y - page relative y coodinate + * @param {?number} x - page relative x coodinate + * @param {?number} y - page relative y coodinate */ - ContextMenu.prototype.open = function (x, y) { + ContextMenu.prototype.open = function (mouseOrLocation) { + var pageX, pageY; + if (typeof mouseOrLocation === MouseEvent) { + pageX = mouseOrLocation.pageX; + pageY = mouseOrLocation.pageY; + } else { + pageX = mouseOrLocation.pageX; + pageY = mouseOrLocation.pageY; + } + // TODO: positioning logic + $(".dropdown").removeClass("open"); $("#" + this.id) .addClass("open") - .css({"left": x, - "top": y - 30}); // todo: compute offset + .css({"left": pageX, + "top": pageY - 30}); // todo: compute offset $(this).triggerHandler("beforeContextMenuOpen"); }; @@ -626,9 +637,8 @@ define(function (require, exports, module) { /** * Registers new context menu with Brackets. * After registering a context menu clients should: - * 1) call yourContextMenu.getMenu() to access to the Menu object - * 2) use addMenuItem() to add items to the Menu object - * 3) call open() to show the context menu (often trigged via an event handler) + * - use addMenuItem() to add items to the context menu + * - call open() to show the context menu (often trigged via an event handler for right click) * * To make menu items be contextual to things like selection listen for the "beforeContextMenuOpen" * to make changes to Command objects before the context menu is shown. MenuItems are views of @@ -757,29 +767,27 @@ define(function (require, exports, module) { * */ var project_cmenu = registerContextMenu("cmenutest1"); - menu = project_cmenu.getMenu(); - menu.addMenuItem("test4", Commands.FILE_OPEN); - menu.addMenuItem("test5", Commands.FILE_CLOSE); - menu.addMenuItem("test6", Commands.FILE_NEW); + project_cmenu.addMenuItem("test4", Commands.FILE_OPEN); + project_cmenu.addMenuItem("test5", Commands.FILE_CLOSE); + project_cmenu.addMenuItem("test6", Commands.FILE_NEW); var editor_cmenu = registerContextMenu("cmenutest2"); - menu = editor_cmenu.getMenu(); - menu.addMenuItem("test0", Commands.SHOW_INLINE_EDITOR); - menu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); - menu.addMenuItem("test2", Commands.EDIT_DUPLICATE); - menu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); + editor_cmenu.addMenuItem("test0", Commands.SHOW_INLINE_EDITOR); + editor_cmenu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); + editor_cmenu.addMenuItem("test2", Commands.EDIT_DUPLICATE); + editor_cmenu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); - $("#projects").mousedown(function (e) { - if (e.which === 3) { - project_cmenu.open(e.pageX, e.pageY); - } - }); + // $("#projects").mousedown(function (e) { + // if (e.which === 3) { + // project_cmenu.open(e); + // } + // }); $("#editor-holder").mousedown(function (e) { if (e.which === 3) { - editor_cmenu.open(e.pageX, e.pageY); + editor_cmenu.open(e); } }); From 1795a55aa43eabebac650d8d066331813ab83103 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Fri, 8 Jun 2012 15:27:57 -0700 Subject: [PATCH 07/35] share styles --- src/command/Menus.js | 12 +-- src/styles/brackets_patterns_override.less | 113 ++------------------- 2 files changed, 13 insertions(+), 112 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index ef5295ea4da..fe382396a3c 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -566,21 +566,13 @@ define(function (require, exports, module) { this.id = id; this.menu = new Menu(id); - // TODO: menuAppearance is a temporary class to make the context menus look correct - // until the CSS code for menus is refactored to support menus and context menus - var $newMenu = $(""); + var $newMenu = $(""); var $toggle = $("") .hide(); - // TODO: move this css into a less file for context menus $newMenu.append($toggle) - .append("") - .css({"position": "absolute", - "z-index": "1000", - "list-style-type": "none", - "top": 200, - "left": 300}); + .append(""); $(window.document.body).append($newMenu); } diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 8a38634d1c8..49bca074dd0 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -157,18 +157,14 @@ } } } - - // HACK: TODO: factor commonalities out of menuAppearance and .toolbar .nav - .menuAppearance { + + +/* Menu-related styles */ +.toolbar .nav, .context-menu { @menubar-top-padding: 8px; @menubar-bottom-padding: 6px; @menubar-h-padding: 9px; @item-highlight-color: #e0f0fa; - - // - .menu-shortcut { - float: right; - } // The 1px adjustments here are to account for the border around the top-level menu items. margin: (@toolbar-top-gap-px - @menubar-top-padding - 1) 0 (-@menubar-bottom-padding + 1) (-@menubar-h-padding + 4); @@ -260,104 +256,17 @@ .menu-shortcut { margin-left: 15px; } - } - -/* Menu-related styles */ -.toolbar .nav { - @menubar-top-padding: 8px; - @menubar-bottom-padding: 6px; - @menubar-h-padding: 9px; - @item-highlight-color: #e0f0fa; - - // The 1px adjustments here are to account for the border around the top-level menu items. - margin: (@toolbar-top-gap-px - @menubar-top-padding - 1) 0 (-@menubar-bottom-padding + 1) (-@menubar-h-padding + 4); - - // General item appearance - a { - color: @bc-black; - text-shadow: none; - } - - // Menubar item appearance - .dropdown-toggle { - padding: @menubar-top-padding @menubar-h-padding @menubar-bottom-padding; - border: 1px solid rgba(0, 0, 0, 0); // transparent border just to hold the layout so it doesn't shift on hover - color: fadeout(@bc-black, 25%); - font-weight: @font-weight-light; - } - .dropdown-toggle:hover, .dropdown.open .dropdown-toggle { - /* Note: we need several selectors for this in order to match the specificity of all the various Bootstrap - color rules we need to override (and Bootstrap has several selectors because the colors really differ - in their defaults). */ - color: @bc-black; - background: @item-highlight-color; - border-color: #dbeaf4; - } - - // no triangle icon after menu items - .dropdown-toggle:after { - border: 0; - margin: 0; - } - - // Menu dropdown appearance - .dropdown-menu { - top: 34px; // Offset from top of menubar item - - background-color: @bc-white; - border: 1px solid #c8c8c8; - .border-radius(0 0 3px 3px); - .box-shadow(0 6px 18px 0 rgba(0, 0, 0, .2)); - - // Menu items - li { - a { - font-size: @menu-item-font-size; - line-height: 18px; - - // Slightly less padding on left to account for checkmark. - // More padding on top than bottom to center font within its bg highlight better. - padding: 2px 10px 0px 6px; - - color: @bc-black; - text-shadow: none; - white-space: nowrap; - .box-shadow(none); - - &:hover { - color: @bc-black; - background: @item-highlight-color; - } - - &.disabled { - color: @bc-gray; +} - &:hover { - background: @bc-white; - } - } +/* Context menu styles */ - /* hidden checkmark for all list item content ensures consistent left spacing */ - &::before { - content: "✓\00a0"; /* non-breaking space */ - visibility: hidden; - } - /* toggle checkmark visibility */ - &.checked::before { - visibility: visible; - } - } - } - - .divider { - background-color: #eaeaea; - border: 0; - } - } +.context-menu { + position: absolute; + z-index: 1000; // TODO: define in @variable + list-style-type: none; - // Ensure a minimum amount of space to the left of the shortcut .menu-shortcut { - margin-left: 15px; + float: right; } } From 1f4dd8400860a52f19b13355949484f0ecc954aa Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Fri, 8 Jun 2012 16:38:45 -0700 Subject: [PATCH 08/35] round top corners of context menus --- src/styles/brackets_mixins.less | 11 +++++++++++ src/styles/brackets_patterns_override.less | 6 +++++- src/styles/brackets_variables.less | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/styles/brackets_mixins.less b/src/styles/brackets_mixins.less index 1b9afdc0fcc..58faff55986 100644 --- a/src/styles/brackets_mixins.less +++ b/src/styles/brackets_mixins.less @@ -100,3 +100,14 @@ -o-transform-origin: @horizontal @vertical; transform-origin: @horizontal @vertical; } + +// Top Border Radius - bootstrap/mixins.less has border-radius() mixin +// for all corners; this just does the top corners +.top-border-radius(@radius: 5px) { + -webkit-border-top-left-radius: @radius; + -webkit-border-top-right-radius: @radius; + -moz-border-radius-topleft: @radius; + -moz-border-radius-topright: @radius; + border-top-left-radius: @radius; + border-top-right-radius: @radius; +} diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 49bca074dd0..755b610ff22 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -262,8 +262,12 @@ .context-menu { position: absolute; - z-index: 1000; // TODO: define in @variable + z-index: @z-index-brackets-context-menu-base; list-style-type: none; + + .dropdown-menu { + .top-border-radius(6px); + } .menu-shortcut { float: right; diff --git a/src/styles/brackets_variables.less b/src/styles/brackets_variables.less index 03fe1247c74..170bf59b3f4 100644 --- a/src/styles/brackets_variables.less +++ b/src/styles/brackets_variables.less @@ -50,3 +50,5 @@ @z-index-brackets-sidebar-resizer: @z-index-brackets-ui + 2; @z-index-brackets-resizer-div: @z-index-brackets-sidebar-resizer + 1; + +@z-index-brackets-context-menu-base: 1000; From a8fb4984e6eaafc0b387f892b0064d5d68f4e983 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Fri, 8 Jun 2012 17:13:51 -0700 Subject: [PATCH 09/35] round all corners of context-menus --- src/styles/brackets_mixins.less | 11 ----------- src/styles/brackets_patterns_override.less | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/styles/brackets_mixins.less b/src/styles/brackets_mixins.less index 58faff55986..1b9afdc0fcc 100644 --- a/src/styles/brackets_mixins.less +++ b/src/styles/brackets_mixins.less @@ -100,14 +100,3 @@ -o-transform-origin: @horizontal @vertical; transform-origin: @horizontal @vertical; } - -// Top Border Radius - bootstrap/mixins.less has border-radius() mixin -// for all corners; this just does the top corners -.top-border-radius(@radius: 5px) { - -webkit-border-top-left-radius: @radius; - -webkit-border-top-right-radius: @radius; - -moz-border-radius-topleft: @radius; - -moz-border-radius-topright: @radius; - border-top-left-radius: @radius; - border-top-right-radius: @radius; -} diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 755b610ff22..9d601e2395f 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -266,7 +266,7 @@ list-style-type: none; .dropdown-menu { - .top-border-radius(6px); + .border-radius(3px); } .menu-shortcut { From 9312bbc850f44eea6de4e06e71cf95f325815157 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Mon, 11 Jun 2012 14:46:50 -0700 Subject: [PATCH 10/35] working --- src/command/Menus.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 66ed20c1760..800fecb0e76 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -568,7 +568,12 @@ define(function (require, exports, module) { // TODO: menuAppearance is a temporary class to make the context menus look correct // until the CSS code for menus is refactored to support menus and context menus - var $newMenu = $(""); + var $newMenu = $("") + .mousedown(function (e) { + + // TY TODO + e.preventDefault(); + }); var $toggle = $("") .hide(); @@ -779,14 +784,15 @@ define(function (require, exports, module) { - // $("#projects").mousedown(function (e) { - // if (e.which === 3) { - // project_cmenu.open(e); - // } - // }); + $("#projects").mousedown(function (e) { + if (e.which === 3) { + project_cmenu.open(e); + } + }); $("#editor-holder").mousedown(function (e) { if (e.which === 3) { + editor_cmenu.open(e); } }); From ef70b2498cb0df735e2d9d10554a8b254b6868be Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Mon, 11 Jun 2012 15:53:33 -0700 Subject: [PATCH 11/35] prevent menus from stealing focus --- src/command/Menus.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index a556f18854f..beb6e5a16a0 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -785,22 +785,22 @@ define(function (require, exports, module) { }); - $("#main-toolbar .dropdown") - // Prevent clicks on the top-level menu bar from taking focus - // Note, bootstrap handles this already for the menu drop downs - .mousedown(function (e) { - e.preventDefault(); - }) - // Switch menus when the mouse enters an adjacent menu - // Only open the menu if another one has already been opened - // by clicking - .mouseenter(function (e) { - var open = $(this).siblings(".open"); - if (open.length > 0) { - open.removeClass("open"); - $(this).addClass("open"); - } - }); + // Prevent clicks on the top-level menu bar from taking focus + // Note, bootstrap handles this already for the menu drop downs + $(document).on("mousedown", ".dropdown", function (e) { + e.preventDefault(); + }); + + // Switch menus when the mouse enters an adjacent menu + // Only open the menu if another one has already been opened + // by clicking + $(document).on("mouseenter", "#main-toolbar .dropdown", function (e) { + var open = $(this).siblings(".open"); + if (open.length > 0) { + open.removeClass("open"); + $(this).addClass("open"); + } + }); } // Define public API From a1b3d82692454e50d904a4dcf10fa24791d8208b Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Mon, 11 Jun 2012 17:28:50 -0700 Subject: [PATCH 12/35] working on right click selection --- src/command/Menus.js | 24 +++++++++++++++++------- src/editor/Editor.js | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index beb6e5a16a0..c70c964b6a6 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -31,6 +31,7 @@ define(function (require, exports, module) { // Load dependent modules var Commands = require("command/Commands"), KeyBindingManager = require("command/KeyBindingManager"), + EditorManager = require("editor/EditorManager"), Strings = require("strings"), StringUtils = require("utils/StringUtils"), CommandManager = require("command/CommandManager"); @@ -763,24 +764,26 @@ define(function (require, exports, module) { project_cmenu.addMenuItem("test5", Commands.FILE_CLOSE); project_cmenu.addMenuItem("test6", Commands.FILE_NEW); - var editor_cmenu = registerContextMenu("cmenutest2"); + var editor_cmenu = registerContextMenu("editorCo"); editor_cmenu.addMenuItem("test0", Commands.SHOW_INLINE_EDITOR); editor_cmenu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); editor_cmenu.addMenuItem("test2", Commands.EDIT_DUPLICATE); editor_cmenu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); - - $("#projects").mousedown(function (e) { + $("#editor-holder").mousedown(function (e) { if (e.which === 3) { - project_cmenu.open(e); + var editor = EditorManager.getFocusedEditor(); + var pos = editor.posFromMouse(e); + editor.selectWordAt(pos); + editor_cmenu.open(e); } }); - $("#editor-holder").mousedown(function (e) { - if (e.which === 3) { - editor_cmenu.open(e); + $("#projects").mousedown(function (e) { + if (e.which === 3) { + project_cmenu.open(e); } }); @@ -790,6 +793,13 @@ define(function (require, exports, module) { $(document).on("mousedown", ".dropdown", function (e) { e.preventDefault(); }); + + // close all dropdowns on ESC + $(document).on("keydown", function (e) { + if (e.keyCode === 27) { + $(".dropdown").removeClass("open"); + } + }); // Switch menus when the mouse enters an adjacent menu // Only open the menu if another one has already been opened diff --git a/src/editor/Editor.js b/src/editor/Editor.js index f5ff28eae27..6405f4e991e 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -672,6 +672,15 @@ define(function (require, exports, module) { Editor.prototype.setCursorPos = function (line, ch) { this._codeMirror.setCursor(line, ch); }; + + Editor.prototype.posFromMouse = function (e, liberal, dirHint) { + return this._codeMirror.posFromMouse(e, liberal, dirHint); + }; + + // TODO TY: move me + Editor.prototype.selectWordAt = function (pos) { + return this._codeMirror.selectWordAt(pos); + }; /** * Gets the current selection. Start is inclusive, end is exclusive. If there is no selection, From 619422868f8a96c01f2f7c992ed7661e4939dcbe Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Mon, 11 Jun 2012 18:32:35 -0700 Subject: [PATCH 13/35] Fixing bug with offsetToLineNum. Tested boundary cases: - query is at char 0 of first line - query is at char 0 of last line - query is at last char of last line - query is at first of last line --- src/utils/StringUtils.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/utils/StringUtils.js b/src/utils/StringUtils.js index fb88c0c6d13..1d06d0729dc 100644 --- a/src/utils/StringUtils.js +++ b/src/utils/StringUtils.js @@ -91,16 +91,23 @@ define(function (require, exports, module) { total = 0, line; for (line = 0; line < lines.length; line++) { - if (total <= offset) { + if (total < offset) { // add 1 per line since /n were removed by splitting, but they needed to // contribute to the total offset count total += lines[line].length + 1; + } else if (total === offset) { + return line; } else { return line - 1; } } - return undefined; + // if offset is NOT over the total then offset is in the last line + if (offset <= total) { + return line - 1; + } else { + return undefined; + } } else { return textOrLines.substr(0, offset).split("\n").length - 1; } From a931ca471f6e5ecb386a6ea30aefcdea4391ee69 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Mon, 11 Jun 2012 21:57:21 -0700 Subject: [PATCH 14/35] id is no longer necessary on MenuItems --- src/command/Menus.js | 194 ++++++++++++++++++++++----------------- src/utils/StringUtils.js | 13 ++- test/spec/Menu-test.js | 69 +++++++------- 3 files changed, 158 insertions(+), 118 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 259708d2323..edf52a32596 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -135,11 +135,11 @@ define(function (require, exports, module) { } function _getHTMLMenu(id) { - return $("#" + id).get(0); + return $("#" + StringUtils.jQueryIdEscape(id)).get(0); } function _getHTMLMenuItem(id) { - return $("#" + id).get(0); + return $("#" + StringUtils.jQueryIdEscape(id)).get(0); } function _addKeyBindingToMenuItem($menuItem, key, displayKey) { @@ -207,8 +207,6 @@ define(function (require, exports, module) { } } - - /** * @constructor * @private @@ -268,6 +266,43 @@ define(function (require, exports, module) { this.id = id; } + /** + * Determine MenuItem a Command id + * + * @param {?string} relativeID - id of command (future: also sub-menu, or menu section). + */ + Menu.prototype._getRelativeMenuItem = function (relativeID) { + var $relativeElement, + key, + menuItem, + map, + 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 (foundMenuItem) { + $relativeElement = $(_getHTMLMenuItem(foundMenuItem.id)).closest("li"); + } + } + } + + return $relativeElement; + }; + /** * Adds a new menu item with the specified id and display text. The insertion position is * specified via the relativeID and position arguments which describe a position @@ -281,37 +316,34 @@ define(function (require, exports, module) { * Note, keyBindings are bound to Command objects not MenuItems. The provided keyBindings * will be bound to the supplied Command object rather than the MenuItem. * - * @param {!string} id * @param {!string | Command} command - the command the menu will execute. * Use DIVIDER for a menu divider * @param {?string | Array.<{key: string, platform: string}>} keyBindings - register one * 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 menuItem, sub-menu, or menu section that the new - * menuItem will be positioned relative to. Required when position is + * @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 * * @return {MenuItem} the newly created MenuItem */ - Menu.prototype.addMenuItem = function (id, command, keyBindings, position, relativeID) { - var $menuItem, + Menu.prototype.addMenuItem = function (command, keyBindings, position, relativeID) { + var id, + $menuItem, $link, menuItem, name, commandID; - if (!id || !command) { - throw new Error("addMenuItem(): missing required parameters. id: " + id); - } - - if (menuItemMap[id]) { - throw new Error("MenuItem added with same id of existing MenuItem: " + id); + if (!command) { + throw new Error("addMenuItem(): missing required parameters: command"); } if (typeof (command) === "string") { if (command === DIVIDER) { name = DIVIDER; + commandID = _getNextMenuItemDividerID(); } else { commandID = command; command = CommandManager.get(commandID); @@ -320,6 +352,15 @@ define(function (require, exports, module) { } name = command.getName(); } + } else { + commandID = command.getID; + } + + // Internal id is the a composite of the parent menu id and the command id. + id = this.id + "-" + commandID; + + if (menuItemMap[id]) { + throw new Error("MenuItem added with same id of existing MenuItem: " + id); } // create MenuItem @@ -339,8 +380,9 @@ define(function (require, exports, module) { } // Insert menu item - var $relativeElement = relativeID && $(_getHTMLMenuItem(relativeID)).closest("li"); - _insertInList($("li#" + this.id + " > ul.dropdown-menu"), $menuItem, position, $relativeElement); + var $relativeElement = this._getRelativeMenuItem(relativeID); + _insertInList($("li#" + StringUtils.jQueryIdEscape(this.id) + " > ul.dropdown-menu"), + $menuItem, position, $relativeElement); // Initialize MenuItem state if (!menuItem.isDivider) { @@ -376,7 +418,7 @@ define(function (require, exports, module) { * @return {MenuItem} the newly created divider */ Menu.prototype.addMenuDivider = function (position, relativeID) { - return this.addMenuItem(_getNextMenuItemDividerID(), DIVIDER, position, relativeID); + return this.addMenuItem(DIVIDER, position, relativeID); }; /** @@ -566,7 +608,8 @@ define(function (require, exports, module) { this.id = id; this.menu = new Menu(id); - var $newMenu = $(""); + var $newMenu = $(""); var $toggle = $("") .hide(); @@ -609,7 +652,7 @@ define(function (require, exports, module) { $(".dropdown").removeClass("open"); - $("#" + this.id) + $("#" + StringUtils.jQueryIdEscape(this.id)) .addClass("open") .css({"left": pageX, "top": pageY - 30}); // todo: compute offset @@ -621,7 +664,7 @@ define(function (require, exports, module) { * Closes the context menu and dispatches the "contextMenuOpen" event. */ ContextMenu.prototype.close = function () { - $("#" + this.id).removeClass("open"); + $("#" + StringUtils.jQueryIdEscape(this.id)).removeClass("open"); $(this).triggerHandler("contextMenuClose"); }; @@ -669,89 +712,77 @@ define(function (require, exports, module) { */ var menu; menu = addMenu(Strings.FILE_MENU, AppMenuBar.FILE_MENU); - menu.addMenuItem("menu-file-new", Commands.FILE_NEW, "Ctrl-N"); - menu.addMenuItem("menu-file-open", Commands.FILE_OPEN, "Ctrl-O"); - menu.addMenuItem("menu-file-open-folder", Commands.FILE_OPEN_FOLDER); - menu.addMenuItem("menu-file-close", Commands.FILE_CLOSE, "Ctrl-W"); + menu.addMenuItem(Commands.FILE_NEW, "Ctrl-N"); + menu.addMenuItem(Commands.FILE_OPEN, "Ctrl-O"); + menu.addMenuItem(Commands.FILE_OPEN_FOLDER); + menu.addMenuItem(Commands.FILE_CLOSE, "Ctrl-W"); menu.addMenuDivider(); - menu.addMenuItem("menu-file-save", Commands.FILE_SAVE, "Ctrl-S"); + menu.addMenuItem(Commands.FILE_SAVE, "Ctrl-S"); menu.addMenuDivider(); - menu.addMenuItem("menu-file-live-preview", Commands.FILE_LIVE_FILE_PREVIEW, - "Ctrl-Alt-P"); + menu.addMenuItem(Commands.FILE_LIVE_FILE_PREVIEW, "Ctrl-Alt-P"); menu.addMenuDivider(); - menu.addMenuItem("menu-file-quit", Commands.FILE_QUIT, "Ctrl-Q"); + menu.addMenuItem(Commands.FILE_QUIT, "Ctrl-Q"); /* * Edit menu */ menu = addMenu(Strings.EDIT_MENU, AppMenuBar.EDIT_MENU); - menu.addMenuItem("menu-edit-select-all", Commands.EDIT_SELECT_ALL, "Ctrl-A"); + menu.addMenuItem(Commands.EDIT_SELECT_ALL, "Ctrl-A"); menu.addMenuDivider(); - menu.addMenuItem("menu-edit-find", Commands.EDIT_FIND, "Ctrl-F"); - menu.addMenuItem("menu-edit-find-in-files", Commands.EDIT_FIND_IN_FILES, - "Ctrl-Shift-F"); - menu.addMenuItem("menu-edit-find-next", Commands.EDIT_FIND_NEXT, - [{key: "F3", platform: "win"}, - {key: "Ctrl-G", platform: "mac"}]); + menu.addMenuItem(Commands.EDIT_FIND, "Ctrl-F"); + menu.addMenuItem(Commands.EDIT_FIND_IN_FILES, "Ctrl-Shift-F"); + menu.addMenuItem(Commands.EDIT_FIND_NEXT, [{key: "F3", platform: "win"}, + {key: "Ctrl-G", platform: "mac"}]); - menu.addMenuItem("menu-edit-find-previous", Commands.EDIT_FIND_PREVIOUS, - [{key: "Shift-F3", platform: "win"}, - {key: "Ctrl-Shift-G", platform: "mac"}]); + menu.addMenuItem(Commands.EDIT_FIND_PREVIOUS, [{key: "Shift-F3", platform: "win"}, + {key: "Ctrl-Shift-G", platform: "mac"}]); menu.addMenuDivider(); - menu.addMenuItem("menu-edit-replace", Commands.EDIT_REPLACE, - [{key: "Ctrl-H", platform: "win"}, - {key: "Ctrl-Alt-F", platform: "mac"}]); + menu.addMenuItem(Commands.EDIT_REPLACE, [{key: "Ctrl-H", platform: "win"}, + {key: "Ctrl-Alt-F", platform: "mac"}]); menu.addMenuDivider(); - menu.addMenuItem("menu-edit-duplicate", Commands.EDIT_DUPLICATE, "Ctrl-D"); - menu.addMenuItem("menu-edit-comment", Commands.EDIT_LINE_COMMENT, "Ctrl-/"); + menu.addMenuItem(Commands.EDIT_DUPLICATE, "Ctrl-D"); + menu.addMenuItem(Commands.EDIT_LINE_COMMENT, "Ctrl-/"); /* * View menu */ menu = addMenu(Strings.VIEW_MENU, AppMenuBar.VIEW_MENU); - menu.addMenuItem("menu-view-sidebar", Commands.VIEW_HIDE_SIDEBAR, "Ctrl-Shift-H"); + menu.addMenuItem(Commands.VIEW_HIDE_SIDEBAR, "Ctrl-Shift-H"); menu.addMenuDivider(); - menu.addMenuItem("menu-view-increase-font", Commands.VIEW_INCREASE_FONT_SIZE, [{key: "Ctrl-=", displayKey: "Ctrl-+"}]); - menu.addMenuItem("menu-view-decrease-font", Commands.VIEW_DECREASE_FONT_SIZE, [{key: "Ctrl--", displayKey: "Ctrl-\u2212"}]); + menu.addMenuItem(Commands.VIEW_INCREASE_FONT_SIZE, [{key: "Ctrl-=", displayKey: "Ctrl-+"}]); + menu.addMenuItem(Commands.VIEW_DECREASE_FONT_SIZE, [{key: "Ctrl--", displayKey: "Ctrl-\u2212"}]); /* * Navigate menu */ menu = addMenu(Strings.NAVIGATE_MENU, AppMenuBar.NAVIGATE_MENU); - menu.addMenuItem("menu-navigate-quick-open", Commands.NAVIGATE_QUICK_OPEN, - "Ctrl-Shift-O"); - menu.addMenuItem("menu-navigate-goto-line", Commands.NAVIGATE_GOTO_LINE, - [{key: "Ctrl-G", platform: "win"}, - {key: "Ctrl-L", platform: "mac"}]); - - menu.addMenuItem("menu-navigate-goto-symbol", Commands.NAVIGATE_GOTO_DEFINITION, - "Ctrl-T"); + menu.addMenuItem(Commands.NAVIGATE_QUICK_OPEN, "Ctrl-Shift-O"); + menu.addMenuItem(Commands.NAVIGATE_GOTO_LINE, [{key: "Ctrl-G", platform: "win"}, + {key: "Ctrl-L", platform: "mac"}]); + + menu.addMenuItem(Commands.NAVIGATE_GOTO_DEFINITION, "Ctrl-T"); menu.addMenuDivider(); - menu.addMenuItem("menu-navigate-quick-edit", Commands.SHOW_INLINE_EDITOR, - "Ctrl-E"); - menu.addMenuItem("menu-navigate-prev-match", Commands.QUICK_EDIT_PREV_MATCH, - {key: "Alt-Up", displayKey: "Alt-\u2191"}); - menu.addMenuItem("menu-navigate-next-match", Commands.QUICK_EDIT_NEXT_MATCH, - {key: "Alt-Down", displayKey: "Alt-\u2193"}); + menu.addMenuItem(Commands.SHOW_INLINE_EDITOR, "Ctrl-E"); + menu.addMenuItem(Commands.QUICK_EDIT_PREV_MATCH, {key: "Alt-Up", displayKey: "Alt-\u2191"}); + menu.addMenuItem(Commands.QUICK_EDIT_NEXT_MATCH, {key: "Alt-Down", displayKey: "Alt-\u2193"}); + /* * Debug menu */ menu = addMenu(Strings.DEBUG_MENU, AppMenuBar.DEBUG_MENU); - menu.addMenuItem("menu-debug-reload-wn", Commands.DEBUG_REFRESH_WINDOW, - [{key: "F5", platform: "win"}, - {key: "Ctrl-R", platform: "mac"}]); - - menu.addMenuItem("menu-debug-dev-tools", Commands.DEBUG_SHOW_DEVELOPER_TOOLS); - menu.addMenuItem("menu-debug-run-tests", Commands.DEBUG_RUN_UNIT_TESTS); - menu.addMenuItem("menu-debug-enable-jslint", Commands.DEBUG_JSLINT); - menu.addMenuItem("menu-debug-perf-data", Commands.DEBUG_SHOW_PERF_DATA); - menu.addMenuDivider(); - menu.addMenuItem("menu-debug-experiemental", Commands.DEBUG_EXPERIMENTAL); - menu.addMenuItem("menu-debug-new-window", Commands.DEBUG_NEW_BRACKETS_WINDOW); - menu.addMenuItem("menu-debug-close-browser", Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS); - menu.addMenuItem("menu-debug-use-tab-chars", Commands.DEBUG_USE_TAB_CHARS); + menu.addMenuItem(Commands.DEBUG_REFRESH_WINDOW, [{key: "F5", platform: "win"}, + {key: "Ctrl-R", platform: "mac"}]); + menu.addMenuItem(Commands.DEBUG_SHOW_DEVELOPER_TOOLS); + menu.addMenuItem(Commands.DEBUG_RUN_UNIT_TESTS); + menu.addMenuItem(Commands.DEBUG_JSLINT); + menu.addMenuItem(Commands.DEBUG_SHOW_PERF_DATA); + menu.addMenuDivider(); + menu.addMenuItem(Commands.DEBUG_EXPERIMENTAL); + menu.addMenuItem(Commands.DEBUG_NEW_BRACKETS_WINDOW); + menu.addMenuItem(Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS); + menu.addMenuItem(Commands.DEBUG_USE_TAB_CHARS); /** @@ -759,17 +790,16 @@ define(function (require, exports, module) { * */ var project_cmenu = registerContextMenu("cmenutest1"); - project_cmenu.addMenuItem("test4", Commands.FILE_OPEN); - project_cmenu.addMenuItem("test5", Commands.FILE_CLOSE); - project_cmenu.addMenuItem("test6", Commands.FILE_NEW); + project_cmenu.addMenuItem(Commands.FILE_OPEN); + project_cmenu.addMenuItem(Commands.FILE_CLOSE); + project_cmenu.addMenuItem(Commands.FILE_NEW); var editor_cmenu = registerContextMenu("cmenutest2"); - editor_cmenu.addMenuItem("test0", Commands.SHOW_INLINE_EDITOR); - editor_cmenu.addMenuItem("test1", Commands.EDIT_SELECT_ALL); - editor_cmenu.addMenuItem("test2", Commands.EDIT_DUPLICATE); - editor_cmenu.addMenuItem("test3", Commands.EDIT_LINE_COMMENT); + editor_cmenu.addMenuItem(Commands.SHOW_INLINE_EDITOR); + editor_cmenu.addMenuItem(Commands.EDIT_SELECT_ALL); + editor_cmenu.addMenuItem(Commands.EDIT_DUPLICATE); + editor_cmenu.addMenuItem(Commands.EDIT_LINE_COMMENT); - // $("#projects").mousedown(function (e) { // if (e.which === 3) { diff --git a/src/utils/StringUtils.js b/src/utils/StringUtils.js index 09d2d4e94bb..9f09a15207a 100644 --- a/src/utils/StringUtils.js +++ b/src/utils/StringUtils.js @@ -44,6 +44,13 @@ define(function (require, exports, module) { return str.replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); } - exports.htmlEscape = htmlEscape; - exports.regexEscape = regexEscape; -}); \ No newline at end of file + // Periods (aka "dots") are allowed in HTML identifiers, but jQuery interprets + // them as the start of a class selector, so they need to be escaped + function jQueryIdEscape(str) { + return str.replace(/\./g, "\\."); + } + + exports.htmlEscape = htmlEscape; + exports.regexEscape = regexEscape; + exports.jQueryIdEscape = jQueryIdEscape; +}); diff --git a/test/spec/Menu-test.js b/test/spec/Menu-test.js index c1c6f197c97..349be3366f8 100644 --- a/test/spec/Menu-test.js +++ b/test/spec/Menu-test.js @@ -32,6 +32,7 @@ define(function (require, exports, module) { KeyBindingManager, Menus, SpecRunnerUtils = require("./SpecRunnerUtils.js"), + StringsUtils = require("utils/StringUtils"), Strings = require("strings"); describe("Menus", function () { @@ -176,14 +177,17 @@ define(function (require, exports, module) { expect($listItems.length).toBe(0); // Re-use commands that are already registered - var menuItem = menu.addMenuItem("menuitem-custom", "custom.command"); + var menuItem = menu.addMenuItem("custom.command"); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$("#menu-custom > ul").children(); expect($listItems.length).toBe(1); expect($($listItems[0]).length).toBe(1); - expect($($listItems[0]).find("a#menuitem-custom").length).toBe(1); + + // Periods (aka "dots") are allowed in HTML identifiers, but jQuery interprets + // them as the start of a class selector, so they need to be escaped + expect($($listItems[0]).find("a#menu-custom-custom\\.command").length).toBe(1); }); }); @@ -192,18 +196,18 @@ define(function (require, exports, module) { CommandManager.register("Command Custom 0", "custom.command0", function () {}); CommandManager.register("Command Custom 1", "custom.command1", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); + var menuItem = menu.addMenuItem("custom.command0"); var listSelector = "#menu-custom > ul"; var $listItems = testWindow.$(listSelector).children(); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1", "Ctrl-Alt-0", Menus.FIRST); + menuItem = menu.addMenuItem("custom.command1", "Ctrl-Alt-0", Menus.FIRST); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$(listSelector).children(); expect($listItems.length).toBe(2); - expect($($listItems[0]).find("a#menuitem-custom-1").length).toBe(1); + expect($($listItems[0]).find("a#menu-custom-custom\\.command1").length).toBe(1); }); }); @@ -212,18 +216,18 @@ define(function (require, exports, module) { CommandManager.register("Command Custom 0", "custom.command0", function () {}); CommandManager.register("Command Custom 1", "custom.command1", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); + var menuItem = menu.addMenuItem("custom.command0"); var listSelector = "#menu-custom > ul"; var $listItems = testWindow.$(listSelector).children(); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1", Menus.LAST); + menuItem = menu.addMenuItem("custom.command1", Menus.LAST); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$(listSelector).children(); expect($listItems.length).toBe(2); - expect($($listItems[1]).find("a#menuitem-custom-1").length).toBe(1); + expect($($listItems[1]).find("a#menu-custom-custom\\.command1").length).toBe(1); }); }); @@ -233,19 +237,19 @@ define(function (require, exports, module) { CommandManager.register("Command Custom 1", "custom.command1", function () {}); CommandManager.register("Command Custom 2", "custom.command2", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0", "Ctrl-Alt-0"); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1", "Ctrl-Alt-1"); + var menuItem = menu.addMenuItem("custom.command0", "Ctrl-Alt-0"); + menuItem = menu.addMenuItem("custom.command1", "Ctrl-Alt-1"); var listSelector = "#menu-custom > ul"; var $listItems = testWindow.$(listSelector).children(); - menuItem = menu.addMenuItem("menuitem-custom-2", "custom.command2", "Ctrl-Alt-2", Menus.AFTER, "menuitem-custom-0"); + menuItem = menu.addMenuItem("custom.command2", "Ctrl-Alt-2", Menus.AFTER, "custom.command0"); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$(listSelector).children(); expect($listItems.length).toBe(3); - expect($($listItems[1]).find("a#menuitem-custom-2").length).toBe(1); + expect($($listItems[1]).find("a#menu-custom-custom\\.command2").length).toBe(1); }); }); @@ -255,19 +259,19 @@ define(function (require, exports, module) { CommandManager.register("Command Custom 1", "custom.command1", function () {}); CommandManager.register("Command Custom 2", "custom.command2", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0", "Ctrl-Alt-0"); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1", "Ctrl-Alt-1"); + var menuItem = menu.addMenuItem("custom.command0", "Ctrl-Alt-0"); + menuItem = menu.addMenuItem("custom.command1", "Ctrl-Alt-1"); var listSelector = "#menu-custom > ul"; var $listItems = testWindow.$(listSelector).children(); - menuItem = menu.addMenuItem("menuitem-custom-2", "custom.command2", "Ctrl-Alt-2", Menus.BEFORE, "menuitem-custom-1"); + menuItem = menu.addMenuItem("custom.command2", "Ctrl-Alt-2", Menus.BEFORE, "custom.command1"); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$(listSelector).children(); expect($listItems.length).toBe(3); - expect($($listItems[1]).find("a#menuitem-custom-2").length).toBe(1); + expect($($listItems[1]).find("a#menu-custom-custom\\.command2").length).toBe(1); }); }); @@ -277,29 +281,28 @@ define(function (require, exports, module) { CommandManager.register("Command Custom 1", "custom.command1", function () {}); CommandManager.register("Command Custom 2", "custom.command2", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0", "Ctrl-Alt-0"); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1", "Ctrl-Alt-1"); + var menuItem = menu.addMenuItem("custom.command0", "Ctrl-Alt-0"); + menuItem = menu.addMenuItem("custom.command1", "Ctrl-Alt-1"); var listSelector = "#menu-custom > ul"; var $listItems = testWindow.$(listSelector).children(); - menuItem = menu.addMenuItem("menuitem-custom-2", "custom.command2", "Ctrl-Alt-2", Menus.BEFORE, "NONEXISTANT"); + menuItem = menu.addMenuItem("custom.command2", "Ctrl-Alt-2", Menus.BEFORE, "NONEXISTANT"); expect(menuItem).not.toBeNull(); expect(menuItem).toBeDefined(); $listItems = testWindow.$(listSelector).children(); expect($listItems.length).toBe(3); - expect($($listItems[2]).find("a#menuitem-custom-2").length).toBe(1); + expect($($listItems[2]).find("a#menu-custom-custom\\.command2").length).toBe(1); }); }); - it("should not add duplicate menu item", function () { + it("should not add menu item for duplicate command in a menu", function () { runs(function () { CommandManager.register("Command Custom 0", "custom.command0", function () {}); - CommandManager.register("Command Custom 1", "custom.command1", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); + var menuItem = menu.addMenuItem("custom.command0"); var $listItems = testWindow.$("#menu-custom > ul").children(); expect($listItems.length).toBe(1); @@ -307,7 +310,7 @@ define(function (require, exports, module) { var exceptionThrown = false; try { menuItem = null; - menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command1"); + menuItem = menu.addMenuItem("custom.command0"); } catch (e) { exceptionThrown = true; } @@ -330,7 +333,7 @@ define(function (require, exports, module) { var menuItem = null; var exceptionThrown = false; try { - menuItem = menu.addMenuItem("menuitem-custom-0", "UNREGISTERED_COMMAND"); + menuItem = menu.addMenuItem("UNREGISTERED_COMMAND"); } catch (e) { exceptionThrown = true; } @@ -350,9 +353,9 @@ define(function (require, exports, module) { var menu = Menus.addMenu("Custom", "menu-custom"); var menuItem = null; - menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); + menuItem = menu.addMenuItem("custom.command0"); menuItem = menu.addMenuDivider(); - menuItem = menu.addMenuItem("menuitem-custom-1", "custom.command1"); + menuItem = menu.addMenuItem("custom.command1"); var $listItems = testWindow.$("#menu-custom > ul").children(); expect($listItems.length).toBe(3); @@ -370,8 +373,8 @@ define(function (require, exports, module) { expect(cmd).toBeDefined(); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); - var menuSelector = "#menuitem-custom-0"; + var menuItem = menu.addMenuItem("custom.command0"); + var menuSelector = "#menu-custom-custom\\.command0"; // Verify menu is synced with command var $menuItem = testWindow.$(menuSelector); @@ -401,8 +404,8 @@ define(function (require, exports, module) { expect(cmd).toBeDefined(); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0"); - var menuSelector = "#menuitem-custom-0"; + var menuItem = menu.addMenuItem("custom.command0"); + var menuSelector = "#menu-custom-custom\\.command0"; // Verify menu is synced with command var $menuItem = testWindow.$(menuSelector); @@ -428,8 +431,8 @@ define(function (require, exports, module) { runs(function () { CommandManager.register("Command Custom 0", "custom.command0", function () {}); var menu = Menus.addMenu("Custom", "menu-custom"); - var menuItem = menu.addMenuItem("menuitem-custom-0", "custom.command0", "Ctrl-9"); - var menuSelector = "#menuitem-custom-0"; + var menuItem = menu.addMenuItem("custom.command0", "Ctrl-9"); + var menuSelector = "#menu-custom-custom\\.command0"; // Verify menu is synced with command var $menuItem = testWindow.$(menuSelector), From a65e5d72973ca1ddac807351d5580b202223067e Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 08:28:03 -0700 Subject: [PATCH 15/35] fix merge --- src/command/Menus.js | 51 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 871b9903fec..3a17cfa7951 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -23,7 +23,7 @@ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, $, brackets, document */ +/*global define, $, brackets, window, MouseEvent */ define(function (require, exports, module) { 'use strict'; @@ -785,16 +785,57 @@ define(function (require, exports, module) { menu.addMenuItem(Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS); menu.addMenuItem(Commands.DEBUG_USE_TAB_CHARS); + + + /** + * Context Menus Test Code + * + */ + var project_cmenu = registerContextMenu("cmenutest1"); + project_cmenu.addMenuItem(Commands.FILE_OPEN); + project_cmenu.addMenuItem(Commands.FILE_CLOSE); + project_cmenu.addMenuItem(Commands.FILE_NEW); + + var editor_cmenu = registerContextMenu("editorCo"); + editor_cmenu.addMenuItem(Commands.SHOW_INLINE_EDITOR); + editor_cmenu.addMenuItem(Commands.EDIT_SELECT_ALL); + editor_cmenu.addMenuItem(Commands.EDIT_DUPLICATE); + editor_cmenu.addMenuItem(Commands.EDIT_LINE_COMMENT); + + + $("#editor-holder").mousedown(function (e) { + if (e.which === 3) { + var editor = EditorManager.getFocusedEditor(); + var pos = editor.posFromMouse(e); + editor.selectWordAt(pos); + editor_cmenu.open(e); + } + }); + + + $("#projects").mousedown(function (e) { + if (e.which === 3) { + project_cmenu.open(e); + } + }); + // Prevent clicks on the top-level menu bar from taking focus - // Note, bootstrap handles this already for the menu drop downs - $(document).on("mousedown", "#main-toolbar .dropdown", function (e) { + // Note, bootstrap handles this already for the menu drop downs + $(window.document).on("mousedown", ".dropdown", function (e) { e.preventDefault(); }); - + + // close all dropdowns on ESC + $(window.document).on("keydown", function (e) { + if (e.keyCode === 27) { + $(".dropdown").removeClass("open"); + } + }); + // Switch menus when the mouse enters an adjacent menu // Only open the menu if another one has already been opened // by clicking - $(document).on("mouseenter", "#main-toolbar .dropdown", function (e) { + $(window.document).on("mouseenter", "#main-toolbar .dropdown", function (e) { var open = $(this).siblings(".open"); if (open.length > 0) { open.removeClass("open"); From ff5308cffa34a66fde95bb31f674b4aef160829c Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 08:51:41 -0700 Subject: [PATCH 16/35] comment out some code causing exceptions --- src/command/Menus.js | 6 +++--- src/editor/Editor.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 3a17cfa7951..43f23079e73 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -805,9 +805,9 @@ define(function (require, exports, module) { $("#editor-holder").mousedown(function (e) { if (e.which === 3) { - var editor = EditorManager.getFocusedEditor(); - var pos = editor.posFromMouse(e); - editor.selectWordAt(pos); +// var editor = EditorManager.getFocusedEditor(); +// var pos = editor.posFromMouse(e); +// editor.selectWordAt(pos); editor_cmenu.open(e); } }); diff --git a/src/editor/Editor.js b/src/editor/Editor.js index 6405f4e991e..9cf75cc2fc8 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -673,9 +673,9 @@ define(function (require, exports, module) { this._codeMirror.setCursor(line, ch); }; - Editor.prototype.posFromMouse = function (e, liberal, dirHint) { - return this._codeMirror.posFromMouse(e, liberal, dirHint); - }; +// Editor.prototype.posFromMouse = function (e, liberal, dirHint) { +// return this._codeMirror.posFromMouse(e, liberal, dirHint); +// }; // TODO TY: move me Editor.prototype.selectWordAt = function (pos) { From 4bfbe71c6a8e0e7ceb62f25ceb8727bfd7cd294e Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 15:52:35 -0700 Subject: [PATCH 17/35] export getContextMenu --- src/command/Menus.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/command/Menus.js b/src/command/Menus.js index 43f23079e73..31333547c65 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -855,6 +855,7 @@ define(function (require, exports, module) { exports.DIVIDER = DIVIDER; exports.getMenu = getMenu; exports.getMenuItem = getMenuItem; + exports.getContextMenu = getContextMenu; exports.addMenu = addMenu; exports.Menu = Menu; exports.MenuItem = MenuItem; From 0de63fa55c3fedf1a24c1b82e57a39a1aedcc0e5 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Tue, 12 Jun 2012 16:43:56 -0700 Subject: [PATCH 18/35] Implement selection rules for right clicking on editor --- src/command/Menus.js | 20 ++++++++++++++++---- src/editor/Editor.js | 29 ++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 43f23079e73..ebf0e59293f 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -656,7 +656,7 @@ define(function (require, exports, module) { $("#" + StringUtils.jQueryIdEscape(this.id)) .addClass("open") .css({"left": pageX, - "top": pageY - 30}); // todo: compute offset + "top": pageY - 20}); $(this).triggerHandler("beforeContextMenuOpen"); }; @@ -805,9 +805,21 @@ define(function (require, exports, module) { $("#editor-holder").mousedown(function (e) { if (e.which === 3) { -// var editor = EditorManager.getFocusedEditor(); -// var pos = editor.posFromMouse(e); -// editor.selectWordAt(pos); + if($(e.target).parents(".CodeMirror-gutter").length !== 0) { + return; + } + + var editor = EditorManager.getFocusedEditor(); + var clickedSel = false, + pos = editor.coordsChar({x: e.pageX, y: e.pageY}); + if (editor.getSelectedText() !== "") { + var sel = editor.getSelection(); + clickedSel = editor.coordsWithinRange(pos, sel.start, sel.end); + } + + if(!clickedSel) { + editor.selectWordAt(pos); + } editor_cmenu.open(e); } }); diff --git a/src/editor/Editor.js b/src/editor/Editor.js index 9cf75cc2fc8..d58956d3e38 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -673,9 +673,32 @@ define(function (require, exports, module) { this._codeMirror.setCursor(line, ch); }; -// Editor.prototype.posFromMouse = function (e, liberal, dirHint) { -// return this._codeMirror.posFromMouse(e, liberal, dirHint); -// }; + /** + * @param {line:number, ch:number} + * @return {number} + */ + Editor.prototype.indexFromPos = function (coords) { + return this._codeMirror.indexFromPos(coords); + }; + + /** + * Returns true if coords is between start and end (inclusive) + * @param {line:number, ch:number} coords + * @param {line:number, ch:number} start + * @param {line:number, ch:number} end + * + */ + Editor.prototype.coordsWithinRange = function(coords, start, end) { + var startIndex = this.indexFromPos(start), + endIndex = this.indexFromPos(end), + coordIndex = this.indexFromPos(coords); + + return coordIndex >= startIndex && coordIndex <= endIndex; + }; + + Editor.prototype.coordsChar = function (coords) { + return this._codeMirror.coordsChar(coords); + }; // TODO TY: move me Editor.prototype.selectWordAt = function (pos) { From 5c2128b06a3c51e97fd369e55e6827dd2cca430a Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Tue, 12 Jun 2012 17:30:34 -0700 Subject: [PATCH 19/35] jslint fixes --- src/command/Menus.js | 6 +++--- src/editor/Editor.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 80105c6132a..5bddad8b29c 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -805,7 +805,7 @@ define(function (require, exports, module) { $("#editor-holder").mousedown(function (e) { if (e.which === 3) { - if($(e.target).parents(".CodeMirror-gutter").length !== 0) { + if ($(e.target).parents(".CodeMirror-gutter").length !== 0) { return; } @@ -814,10 +814,10 @@ define(function (require, exports, module) { pos = editor.coordsChar({x: e.pageX, y: e.pageY}); if (editor.getSelectedText() !== "") { var sel = editor.getSelection(); - clickedSel = editor.coordsWithinRange(pos, sel.start, sel.end); + clickedSel = editor.coordsWithinRange(pos, sel.start, sel.end); } - if(!clickedSel) { + if (!clickedSel) { editor.selectWordAt(pos); } editor_cmenu.open(e); diff --git a/src/editor/Editor.js b/src/editor/Editor.js index d58956d3e38..336644ceff9 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -688,7 +688,7 @@ define(function (require, exports, module) { * @param {line:number, ch:number} end * */ - Editor.prototype.coordsWithinRange = function(coords, start, end) { + Editor.prototype.coordsWithinRange = function (coords, start, end) { var startIndex = this.indexFromPos(start), endIndex = this.indexFromPos(end), coordIndex = this.indexFromPos(coords); From 70517d882ada8b65196a67474f502fb028383662 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 17:50:20 -0700 Subject: [PATCH 20/35] add extension to test context menus --- .../disabled/ContextMenuTest/main.js | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/extensions/disabled/ContextMenuTest/main.js diff --git a/src/extensions/disabled/ContextMenuTest/main.js b/src/extensions/disabled/ContextMenuTest/main.js new file mode 100644 index 00000000000..d0de1cded41 --- /dev/null +++ b/src/extensions/disabled/ContextMenuTest/main.js @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. +* + * Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: +* + * The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +* + */ + + +/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ +/*global define, brackets, $ */ + +define(function (require, exports, module) { + 'use strict'; + + // Brackets modules + var CommandManager = brackets.getModule("command/CommandManager"), + EditorManager = brackets.getModule("editor/EditorManager"), + DocumentManager = brackets.getModule("document/DocumentManager"), + Menus = brackets.getModule("command/Menus"); + + function TestCommand1() { + var command1 = CommandManager.get("custom.command1"); + if (!command1) { + return; + } + var command2 = CommandManager.get("custom.command2"); + if (!command2) { + return; + } + + var checked = command1.getChecked(); + if (checked) { + alert("Unchecking and Disabling next"); + command2.setEnabled(false); + } else { + alert("Checking and Enabling next"); + command2.setEnabled(true); + } + command1.setChecked(!checked); + } + + function TestCommand2() { + alert("Executing command 2"); + } + + function TestCommand3() { + alert("Executing command 3"); + } + + // Create command + var command1 = CommandManager.register("Toggle Checkmark", "custom.command1", TestCommand1); + var command2 = CommandManager.register("Enabled when previous is Checked", "custom.command2", TestCommand2); + var command3 = CommandManager.register("Enabled when text selected", "custom.command3", TestCommand3); + + command1.setChecked(true); + command2.setEnabled(true); + command3.setEnabled(false); + + + var handleDocChanged = function () { + var editor = EditorManager.getCurrentFullEditor(); + + var handleEnableState = function () { + command3.setEnabled(editor.getSelectedText() !== ""); + }; + + if (editor) { + $(editor).off("cursorActivity", handleEnableState); + $(editor).on("cursorActivity", handleEnableState); + } + }; + + $(DocumentManager).on("currentDocumentChange", handleDocChanged); + handleDocChanged(); + + + // Get editor context menu + var editor_cmenu = Menus.getContextMenu("editorCo"); + + // Add our MenuItem at the end + if (editor_cmenu) { + editor_cmenu.addMenuDivider(); + editor_cmenu.addMenuItem("custom.command1"); + editor_cmenu.addMenuItem("custom.command2"); + editor_cmenu.addMenuItem("custom.command3"); + } +}); From 9229e5d8b44102b2520172b84e5d84b65159ded9 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 18:04:26 -0700 Subject: [PATCH 21/35] fix alignment --- .../disabled/ContextMenuTest/main.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/extensions/disabled/ContextMenuTest/main.js b/src/extensions/disabled/ContextMenuTest/main.js index d0de1cded41..a2f706ccabc 100644 --- a/src/extensions/disabled/ContextMenuTest/main.js +++ b/src/extensions/disabled/ContextMenuTest/main.js @@ -1,24 +1,24 @@ /* -* Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. -* + * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. + * * Permission is hereby granted, free of charge, to any person obtaining a -* copy of this software and associated documentation files (the "Software"), + * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: -* + * * The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* + * all copies or substantial portions of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. -* + * */ From 554e3453228d43353e50257f4ddde03157cfcc28 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 12 Jun 2012 19:06:12 -0700 Subject: [PATCH 22/35] fix comment --- src/command/Menus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 5bddad8b29c..f40eedba337 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -268,7 +268,7 @@ define(function (require, exports, module) { } /** - * Determine MenuItem a Command id + * Determine relative MenuItem * * @param {?string} relativeID - id of command (future: also sub-menu, or menu section). */ From 47d587f1dc6c0dfca044854b2c7de7d8c52d2659 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Wed, 13 Jun 2012 10:58:35 -0700 Subject: [PATCH 23/35] no need to escape id when building a dom node. fix typos. --- src/command/Menus.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index f40eedba337..b774958eac6 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -609,8 +609,7 @@ define(function (require, exports, module) { this.id = id; this.menu = new Menu(id); - var $newMenu = $(""); + var $newMenu = $(""); var $toggle = $("") .hide(); @@ -676,9 +675,9 @@ define(function (require, exports, module) { * - use addMenuItem() to add items to the context menu * - call open() to show the context menu (often trigged via an event handler for right click) * - * To make menu items be contextual to things like selection listen for the "beforeContextMenuOpen" + * To make menu items be contextual to things like selection, listen for the "beforeContextMenuOpen" * to make changes to Command objects before the context menu is shown. MenuItems are views of - * Commands and control a MenuItem's name, enabled state, and checked stae. + * Commands, which control a MenuItem's name, enabled state, and checked state. * * @param {string} id * @return {ContextMenu} the newly created context menu From 795c1ef2b6e7ae26fd050d9a5e46ad0ff74a39a1 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 11:09:50 -0700 Subject: [PATCH 24/35] added todo --- src/command/Menus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 5bddad8b29c..be37af7febb 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -802,7 +802,7 @@ define(function (require, exports, module) { editor_cmenu.addMenuItem(Commands.EDIT_DUPLICATE); editor_cmenu.addMenuItem(Commands.EDIT_LINE_COMMENT); - + // TODO: doesn't word select when changing editors with right click $("#editor-holder").mousedown(function (e) { if (e.which === 3) { if ($(e.target).parents(".CodeMirror-gutter").length !== 0) { From ee4cfad3b059e745e45bb59e2d58c2afc3634ded Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Wed, 13 Jun 2012 14:05:36 -0700 Subject: [PATCH 25/35] add UL container for context menus --- src/command/Menus.js | 2 +- src/extensions/disabled/ContextMenuTest/main.js | 4 ++-- src/index.html | 3 +++ src/styles/brackets_patterns_override.less | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 67912dd5faa..9336e905ed8 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -617,7 +617,7 @@ define(function (require, exports, module) { $newMenu.append($toggle) .append(""); - $(window.document.body).append($newMenu); + $("#context-menu-bar > ul").append($newMenu); } ContextMenu.prototype = new Menu(); ContextMenu.prototype.constructor = ContextMenu; diff --git a/src/extensions/disabled/ContextMenuTest/main.js b/src/extensions/disabled/ContextMenuTest/main.js index a2f706ccabc..4062d3f503a 100644 --- a/src/extensions/disabled/ContextMenuTest/main.js +++ b/src/extensions/disabled/ContextMenuTest/main.js @@ -46,10 +46,10 @@ define(function (require, exports, module) { var checked = command1.getChecked(); if (checked) { - alert("Unchecking and Disabling next"); + alert("Unchecking self. Disabling next."); command2.setEnabled(false); } else { - alert("Checking and Enabling next"); + alert("Checking self. Enabling next."); command2.setEnabled(true); } command1.setChecked(!checked); diff --git a/src/index.html b/src/index.html index e46ad49803e..aa3d00bd57e 100644 --- a/src/index.html +++ b/src/index.html @@ -210,5 +210,8 @@

Brackets

Close +
+
    +
    diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 9d601e2395f..3abc866d104 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -274,6 +274,10 @@ } } +#context-menu-bar { + margin: 0; +} + /* Dialog-related styles */ .modal-footer .btn.left { From 723183a0ddcbe3c241c51addb12ff85e96cb1f0a Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Wed, 13 Jun 2012 14:13:42 -0700 Subject: [PATCH 26/35] udpate comments --- src/command/Menus.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 9336e905ed8..9ccc0d9516e 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -626,7 +626,7 @@ define(function (require, exports, module) { /** * Displays the ContextMenu at the specified location and dispatches the - * "contextMenuOpen" event.The menu location may be adjusted to prevent + * "beforeContextMenuOpen" event.The menu location may be adjusted to prevent * clipping by the browser window. All other menus and ContextMenus will be closed * bofore a new menu is shown. * @@ -661,7 +661,7 @@ define(function (require, exports, module) { }; /** - * Closes the context menu and dispatches the "contextMenuOpen" event. + * Closes the context menu and dispatches the "contextMenuClose" event. */ ContextMenu.prototype.close = function () { $("#" + StringUtils.jQueryIdEscape(this.id)).removeClass("open"); From 759390ce1a215c87d631a2b8f5819db3c34d09a0 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 15:33:46 -0700 Subject: [PATCH 27/35] code review fixes --- src/command/Menus.js | 16 +++------------- src/editor/Editor.js | 15 +++++++++------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 67912dd5faa..50f477ab747 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -590,7 +590,6 @@ define(function (require, exports, module) { /** * @constructor * @extends {Menu} - * @private * * Represents a context menu that can open at a specific location in the UI. * @@ -609,7 +608,7 @@ define(function (require, exports, module) { this.id = id; this.menu = new Menu(id); - var $newMenu = $(""); + var $newMenu = $(""); var $toggle = $("") .hide(); @@ -638,15 +637,6 @@ define(function (require, exports, module) { * @param {?number} y - page relative y coodinate */ ContextMenu.prototype.open = function (mouseOrLocation) { - var pageX, pageY; - if (typeof mouseOrLocation === MouseEvent) { - pageX = mouseOrLocation.pageX; - pageY = mouseOrLocation.pageY; - } else { - pageX = mouseOrLocation.pageX; - pageY = mouseOrLocation.pageY; - } - // TODO: positioning logic @@ -654,8 +644,8 @@ define(function (require, exports, module) { $("#" + StringUtils.jQueryIdEscape(this.id)) .addClass("open") - .css({"left": pageX, - "top": pageY - 20}); + .css({"left": mouseOrLocation.pageX, + "top": mouseOrLocation.pageY - 20}); $(this).triggerHandler("beforeContextMenuOpen"); }; diff --git a/src/editor/Editor.js b/src/editor/Editor.js index 1f6b5980f17..4df468ead88 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -700,12 +700,7 @@ define(function (require, exports, module) { Editor.prototype.coordsChar = function (coords) { return this._codeMirror.coordsChar(coords); }; - - // TODO TY: move me - Editor.prototype.selectWordAt = function (pos) { - return this._codeMirror.selectWordAt(pos); - }; - + /** * Gets the current selection. Start is inclusive, end is exclusive. If there is no selection, * returns the current cursor position as both the start and end of the range (i.e. a selection @@ -736,6 +731,14 @@ define(function (require, exports, module) { this._codeMirror.setSelection(start, end); }; + /** + * Selects a the word nearest to the position + * @param {line:number, ch:number} + */ + Editor.prototype.selectWordAt = function (pos) { + this._codeMirror.selectWordAt(pos); + }; + /** * Gets the total number of lines in the the document (includes lines not visible in the viewport) From e1ca09f82f0f840d99c8954872a0aea54eba88bf Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 16:11:17 -0700 Subject: [PATCH 28/35] review fixes --- src/command/Menus.js | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 15148e94d5d..4150728f91c 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -548,14 +548,16 @@ define(function (require, exports, module) { * Adds a top-level menu to the application menu bar which may be native or HTML-based. * * @param {!string} name - display text for menu - * @param {!string} id + * @param {!string} id - unique identifier for a menu. + * Core Menus in Brackets use a simple title as an id, for example "file-menu". + * Extensions should use the following format: "author.myextension.mymenuname". * @param {?string} position - constant defining the position of new the Menu relative * to other Menus. Default is LAST (see Insertion position constants). * * @param {?string} relativeID - id of Menu the new Menu will be positioned relative to. Required * when position is AFTER or BEFORE, ignored when position is FIRST or LAST * - * @return {Menu} the newly created Menu + * @return {?Menu} the newly created Menu */ function addMenu(name, id, position, relativeID) { name = StringUtils.htmlEscape(name); @@ -568,7 +570,8 @@ define(function (require, exports, module) { // Guard against duplicate menu ids if (menuMap[id]) { - throw new Error("Menu added with same name and id of existing Menu: " + id); + console.log("Menu added with same name and id of existing Menu: " + id); + return null; } menu = new Menu(id); @@ -639,15 +642,16 @@ define(function (require, exports, module) { ContextMenu.prototype.open = function (mouseOrLocation) { // TODO: positioning logic + $(this).triggerHandler("beforeContextMenuOpen"); + // close all other dropdowns $(".dropdown").removeClass("open"); + // open the context menu at specified loation $("#" + StringUtils.jQueryIdEscape(this.id)) .addClass("open") .css({"left": mouseOrLocation.pageX, "top": mouseOrLocation.pageY - 20}); - - $(this).triggerHandler("beforeContextMenuOpen"); }; /** @@ -661,6 +665,10 @@ define(function (require, exports, module) { /** * Registers new context menu with Brackets. + + * Extensions should generally use the predefined context menus built into Brackets. Use this + * API to add a new context menu specific to UI that is specific to an extension. + * * After registering a context menu clients should: * - use addMenuItem() to add items to the context menu * - call open() to show the context menu (often trigged via an event handler for right click) @@ -669,8 +677,10 @@ define(function (require, exports, module) { * to make changes to Command objects before the context menu is shown. MenuItems are views of * Commands, which control a MenuItem's name, enabled state, and checked state. * - * @param {string} id - * @return {ContextMenu} the newly created context menu + * @param {string} id - unique identifier for context menu. + * Core context menus in Brackets use a simple title as an id. + * Extensions should use the following format: "author.myextension.mycontextmenu name" + * @return {?ContextMenu} the newly created context menu */ function registerContextMenu(id) { if (!id) { @@ -679,7 +689,8 @@ define(function (require, exports, module) { // Guard against duplicate menu ids if (contextMenuMap[id]) { - throw new Error("Context Menu added with same name and id of existing Context Menu: " + id); + console.log("Context Menu added with same name and id of existing Context Menu: " + id); + return null; } var cmenu = new ContextMenu(id); From 4ee0aa2d861fdf78234f3a7b80d36c5f84e641ef Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Wed, 13 Jun 2012 16:11:37 -0700 Subject: [PATCH 29/35] cleanup default context menus --- src/command/Menus.js | 21 ++++++++++++------- .../disabled/ContextMenuTest/main.js | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index 15148e94d5d..5c321a48428 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -48,7 +48,14 @@ define(function (require, exports, module) { DEBUG_MENU: "debug-menu" }; - + /** + * Brackets Context Menu Constants + * @enum {string} + */ + var ContextMenuIds = { + EDITOR_MENU: "editor-context-menu", + PROJECT_MENU: "project-context-menu" + }; /** @@ -773,24 +780,23 @@ define(function (require, exports, module) { menu.addMenuItem(Commands.DEBUG_NEW_BRACKETS_WINDOW); menu.addMenuItem(Commands.DEBUG_CLOSE_ALL_LIVE_BROWSERS); menu.addMenuItem(Commands.DEBUG_USE_TAB_CHARS); - - /** - * Context Menus Test Code - * + /* + * Context Menus */ - var project_cmenu = registerContextMenu("cmenutest1"); + var project_cmenu = registerContextMenu(ContextMenuIds.PROJECT_MENU); project_cmenu.addMenuItem(Commands.FILE_OPEN); project_cmenu.addMenuItem(Commands.FILE_CLOSE); project_cmenu.addMenuItem(Commands.FILE_NEW); - var editor_cmenu = registerContextMenu("editorCo"); + var editor_cmenu = registerContextMenu(ContextMenuIds.EDITOR_MENU); editor_cmenu.addMenuItem(Commands.SHOW_INLINE_EDITOR); editor_cmenu.addMenuItem(Commands.EDIT_SELECT_ALL); editor_cmenu.addMenuItem(Commands.EDIT_DUPLICATE); editor_cmenu.addMenuItem(Commands.EDIT_LINE_COMMENT); + // TODO: doesn't word select when changing editors with right click $("#editor-holder").mousedown(function (e) { if (e.which === 3) { @@ -848,6 +854,7 @@ define(function (require, exports, module) { // Define public API exports.init = init; exports.AppMenuBar = AppMenuBar; + exports.ContextMenuIds = ContextMenuIds; exports.MenuSection = MenuSection; exports.BEFORE = BEFORE; exports.AFTER = AFTER; diff --git a/src/extensions/disabled/ContextMenuTest/main.js b/src/extensions/disabled/ContextMenuTest/main.js index 4062d3f503a..267dcb5d096 100644 --- a/src/extensions/disabled/ContextMenuTest/main.js +++ b/src/extensions/disabled/ContextMenuTest/main.js @@ -91,7 +91,7 @@ define(function (require, exports, module) { // Get editor context menu - var editor_cmenu = Menus.getContextMenu("editorCo"); + var editor_cmenu = Menus.getContextMenu(Menus.ContextMenuIds.EDITOR_MENU); // Add our MenuItem at the end if (editor_cmenu) { From 0948a6c677288c5e6e9a155f91e417b16747ae37 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 16:17:50 -0700 Subject: [PATCH 30/35] use focused editor in ContextMenuTest --- src/extensions/disabled/ContextMenuTest/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/disabled/ContextMenuTest/main.js b/src/extensions/disabled/ContextMenuTest/main.js index 4062d3f503a..919bfb6ace0 100644 --- a/src/extensions/disabled/ContextMenuTest/main.js +++ b/src/extensions/disabled/ContextMenuTest/main.js @@ -74,7 +74,7 @@ define(function (require, exports, module) { var handleDocChanged = function () { - var editor = EditorManager.getCurrentFullEditor(); + var editor = EditorManager.getFocusedEditor(); var handleEnableState = function () { command3.setEnabled(editor.getSelectedText() !== ""); From 3b3df72c630d441007dd7b281bf58fe625877a1d Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 17:14:31 -0700 Subject: [PATCH 31/35] code review fixes --- src/command/Menus.js | 30 ++++++++++++++++++------------ src/project/WorkingSetView.js | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index e7f4e07bcba..39559d914d1 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -654,11 +654,15 @@ define(function (require, exports, module) { // close all other dropdowns $(".dropdown").removeClass("open"); - // open the context menu at specified loation - $("#" + StringUtils.jQueryIdEscape(this.id)) - .addClass("open") - .css({"left": mouseOrLocation.pageX, - "top": mouseOrLocation.pageY - 20}); + // open the context menu at specified location + + var menu = $("#" + StringUtils.jQueryIdEscape(this.id)); + // only show context menu if it has items + if (menu.find("ul").children().length > 0) { + menu.addClass("open") + .css({"left": mouseOrLocation.pageX, + "top": mouseOrLocation.pageY - 20}); + } }; /** @@ -797,18 +801,20 @@ define(function (require, exports, module) { * Context Menus */ var project_cmenu = registerContextMenu(ContextMenuIds.PROJECT_MENU); - project_cmenu.addMenuItem(Commands.FILE_OPEN); - project_cmenu.addMenuItem(Commands.FILE_CLOSE); - project_cmenu.addMenuItem(Commands.FILE_NEW); + var open_files_cmenu = registerContextMenu(ContextMenuIds.OPEN_FILES_MENU); var editor_cmenu = registerContextMenu(ContextMenuIds.EDITOR_MENU); editor_cmenu.addMenuItem(Commands.SHOW_INLINE_EDITOR); editor_cmenu.addMenuItem(Commands.EDIT_SELECT_ALL); - editor_cmenu.addMenuItem(Commands.EDIT_DUPLICATE); - editor_cmenu.addMenuItem(Commands.EDIT_LINE_COMMENT); - - // TODO: doesn't word select when changing editors with right click + /** + * Displays context menu when right clicking editor. + * Auto selects the word the user clicks if the click does not occur over + * an existing selection + * + * TODO: doesn't word select when changing editors with right click + * + */ $("#editor-holder").mousedown(function (e) { if (e.which === 3) { if ($(e.target).parents(".CodeMirror-gutter").length !== 0) { diff --git a/src/project/WorkingSetView.js b/src/project/WorkingSetView.js index 3907bf4ca92..d99e4d81645 100644 --- a/src/project/WorkingSetView.js +++ b/src/project/WorkingSetView.js @@ -167,7 +167,7 @@ define(function (require, exports, module) { _updateFileStatusIcon($newItem, isOpenAndDirty(file), false); _updateListItemSelection($newItem, curDoc); - $newItem.click(function () { + $newItem.mousedown(function () { FileViewController.openAndSelectDocument(file.fullPath, FileViewController.WORKING_SET_VIEW); }); From b26127aec54a08c4b616080f42b6798b1621d9de Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 17:51:33 -0700 Subject: [PATCH 32/35] code review fixes --- src/command/CommandManager.js | 5 +++-- src/command/Menus.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/command/CommandManager.js b/src/command/CommandManager.js index 7d788fb240c..282099b0a27 100644 --- a/src/command/CommandManager.js +++ b/src/command/CommandManager.js @@ -166,11 +166,12 @@ define(function (require, exports, module) { * execute() (after the id) are passed as arguments to the function. If the function is asynchronous, * it must return a jQuery promise that is resolved when the command completes. Otherwise, the * CommandManager will assume it is synchronous, and return a promise that is already resolved. - * @return {Command} + * @return {?Command} */ function register(name, id, commandFn) { if (_commands[id]) { - throw new Error("Attempting to register an already-registered command: " + id); + console.log("Attempting to register an already-registered command: " + id); + return null; } if (!name || !id || !commandFn) { throw new Error("Attempting to register a command with a missing name, id, or command function:" + name + " " + id); diff --git a/src/command/Menus.js b/src/command/Menus.js index 39559d914d1..f651ccb9721 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -657,7 +657,7 @@ define(function (require, exports, module) { // open the context menu at specified location var menu = $("#" + StringUtils.jQueryIdEscape(this.id)); - // only show context menu if it has items + // only show context menu if it has menu items if (menu.find("ul").children().length > 0) { menu.addClass("open") .css({"left": mouseOrLocation.pageX, From 1503b63676bb5b576174cf032278258645334807 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 18:10:34 -0700 Subject: [PATCH 33/35] keep only project context menu for now --- src/command/Menus.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index f651ccb9721..d0f4445526b 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -801,7 +801,6 @@ define(function (require, exports, module) { * Context Menus */ var project_cmenu = registerContextMenu(ContextMenuIds.PROJECT_MENU); - var open_files_cmenu = registerContextMenu(ContextMenuIds.OPEN_FILES_MENU); var editor_cmenu = registerContextMenu(ContextMenuIds.EDITOR_MENU); editor_cmenu.addMenuItem(Commands.SHOW_INLINE_EDITOR); From f65b33e3ada655970e17afda322e3fefffa50ad9 Mon Sep 17 00:00:00 2001 From: Ty Voliter Date: Wed, 13 Jun 2012 18:19:03 -0700 Subject: [PATCH 34/35] Updating CommandManager and Menu tests since duplicate id's returns null instead of throwing --- test/spec/CommandManager-test.js | 2 +- test/spec/Menu-test.js | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/spec/CommandManager-test.js b/test/spec/CommandManager-test.js index fb9354b43d4..b5e9bf5caa2 100644 --- a/test/spec/CommandManager-test.js +++ b/test/spec/CommandManager-test.js @@ -57,7 +57,7 @@ define(function (require, exports, module) { expect(command._commandFn).toBe(testCommandFn); // duplicate command - expect(function () { CommandManager.register("test command", commandID, testCommandFn); }).toThrow(); + expect(CommandManager.register("test command", commandID, testCommandFn)).toBeFalsy(); // missing arguments expect(function () { CommandManager.register(null, "test-command-id2", testCommandFn); }).toThrow(); diff --git a/test/spec/Menu-test.js b/test/spec/Menu-test.js index 349be3366f8..f8edad231a2 100644 --- a/test/spec/Menu-test.js +++ b/test/spec/Menu-test.js @@ -151,13 +151,9 @@ define(function (require, exports, module) { expect(menu1).not.toBeNull(); var menu2 = null; - var exceptionThrown = false; - try { - menu2 = Menus.addMenu("Custom", "menu-custom"); - } catch (e) { - exceptionThrown = true; - } - expect(exceptionThrown).toBeTruthy(); + + menu2 = Menus.addMenu("Custom", "menu-custom"); + expect(menu2).toBeFalsy(); $listItems = testWindow.$("#main-toolbar > ul.nav").children(); expect($listItems.length).toBe(menuCountOriginal + 1); From 5a4d99e1c4c7e766eb15f7880b0919a806051dcf Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Wed, 13 Jun 2012 20:19:23 -0700 Subject: [PATCH 35/35] update comment --- src/command/Menus.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/command/Menus.js b/src/command/Menus.js index dcb1b108c50..30936f76b4c 100644 --- a/src/command/Menus.js +++ b/src/command/Menus.js @@ -642,9 +642,6 @@ define(function (require, exports, module) { * @param {MouseEvent | {pageX:number, pageY:number}} mouseOrLocation - pass a MouseEvent * to display the menu near the mouse or pass in an object with page x/y coordinates * for a specific location. - * - * @param {?number} x - page relative x coodinate - * @param {?number} y - page relative y coodinate */ ContextMenu.prototype.open = function (mouseOrLocation) { // TODO: positioning logic