Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1262 from adobe/jason-sanjose/esc-key
Browse files Browse the repository at this point in the history
Re-reviewed. Looks good
  • Loading branch information
tvoliter committed Jul 17, 2012
2 parents a174091 + 28d5de7 commit 57d0b54
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 28 deletions.
48 changes: 23 additions & 25 deletions src/command/Menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ define(function (require, exports, module) {
EditorManager = require("editor/EditorManager"),
Strings = require("strings"),
StringUtils = require("utils/StringUtils"),
CommandManager = require("command/CommandManager");
CommandManager = require("command/CommandManager"),
PopUpManager = require("widgets/PopUpManager");

/**
* Brackets Application Menu Constants
Expand Down Expand Up @@ -600,6 +601,13 @@ define(function (require, exports, module) {
}
}
};

/**
* Closes all menus that are open
*/
function closeAll() {
$(".dropdown").removeClass("open");
}

/**
* Adds a top-level menu to the application menu bar which may be native or HTML-based.
Expand Down Expand Up @@ -634,26 +642,22 @@ define(function (require, exports, module) {
menu = new Menu(id);
menuMap[id] = menu;

var $newMenu = $("<li class='dropdown' id='" + id + "'></li>")
.append("<a href='#' class='dropdown-toggle'>" + name + "</a>")
.append("<ul class='dropdown-menu'></ul>");
var $toggle = $("<a href='#' class='dropdown-toggle'>" + name + "</a>"),
$popUp = $("<ul class='dropdown-menu'></ul>"),
$newMenu = $("<li class='dropdown' id='" + id + "'></li>").append($toggle).append($popUp);

// Insert menu
var $relativeElement = relativeID && $(_getHTMLMenu(relativeID));
_insertInList($menubar, $newMenu, position, $relativeElement);

// Install ESC key handling
PopUpManager.configurePopUp($popUp, closeAll);

// todo error handling

return menu;
}

/**
* Closes all menus that are open
*/
function closeAll() {
$(".dropdown").removeClass("open");
}

/**
* @constructor
* @extends {Menu}
Expand All @@ -675,15 +679,17 @@ define(function (require, exports, module) {
this.id = id;
this.menu = new Menu(id);

var $newMenu = $("<li class='dropdown context-menu' id='" + StringUtils.jQueryIdEscape(id) + "'></li>");

var $toggle = $("<a href='#' class='dropdown-toggle'></a>")
.hide();
var $newMenu = $("<li class='dropdown context-menu' id='" + StringUtils.jQueryIdEscape(id) + "'></li>"),
$popUp = $("<ul class='dropdown-menu'></ul>"),
$toggle = $("<a href='#' class='dropdown-toggle'></a>").hide();

$newMenu.append($toggle)
.append("<ul class='dropdown-menu'></ul>");
// assemble the menu fragments
$newMenu.append($toggle).append($popUp);

// insert into DOM
$("#context-menu-bar > ul").append($newMenu);

PopUpManager.configurePopUp($popUp, closeAll);
}
ContextMenu.prototype = new Menu();
ContextMenu.prototype.constructor = ContextMenu;
Expand Down Expand Up @@ -951,7 +957,6 @@ define(function (require, exports, module) {
e.preventDefault();
});


/*
* General menu event processing
*/
Expand All @@ -960,13 +965,6 @@ define(function (require, exports, module) {
e.preventDefault();
});

// close all dropdowns on ESC
$(window.document).on("keydown", function (e) {
if (e.keyCode === 27) {
closeAll();
}
});

// Switch menus when the mouse enters an adjacent menu
// Only open the menu if another one has already been opened
// by clicking
Expand Down
15 changes: 12 additions & 3 deletions src/editor/CodeHintManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ define(function (require, exports, module) {
Menus = require("command/Menus"),
StringUtils = require("utils/StringUtils"),
HTMLTags = require("text!CodeHints/HtmlTags.json"),
EditorManager = require("editor/EditorManager");
EditorManager = require("editor/EditorManager"),
PopUpManager = require("widgets/PopUpManager");

/**
* @private
Expand Down Expand Up @@ -280,19 +281,27 @@ define(function (require, exports, module) {
this.updateList();

if (this.displayList.length) {
var hintPos = this.calcHintListLocation();
var self = this,
hintPos = this.calcHintListLocation();

this.$hintMenu.addClass("open")
.css({"left": hintPos.left, "top": hintPos.top});
this.opened = true;

PopUpManager.addPopUp(this.$hintMenu, function () {
self.close();
});
}
};

/**
* Closes the hint list
*/
CodeHintList.prototype.close = function () {
Menus.closeAll();
this.$hintMenu.removeClass("open");
this.opened = false;

PopUpManager.removePopUp(this.$hintMenu);
};

/**
Expand Down
15 changes: 15 additions & 0 deletions src/editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ define(function (require, exports, module) {
CodeMirror.commands.delCharRight(instance);
}
},
"Esc": function (instance) {
self.removeAllInlineWidgets();
},
"Shift-Delete": "cut",
"Ctrl-Insert": "copy",
"Shift-Insert": "paste"
Expand Down Expand Up @@ -845,6 +848,18 @@ define(function (require, exports, module) {
this._fireWidgetOffsetTopChanged(pos.line);
};

/**
* Removes all inline widgets
*/
Editor.prototype.removeAllInlineWidgets = function () {
// copy the array because _removeInlineWidgetInternal will modifying the original
var widgets = [].concat(this.getInlineWidgets());

widgets.forEach(function (widget) {
this.removeInlineWidget(widget);
}, this);
};

/**
* Removes the given inline widget.
* @param {number} inlineWidget The widget to remove.
Expand Down
9 changes: 9 additions & 0 deletions src/editor/InlineWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,20 @@ define(function (require, exports, module) {
*
*/
function InlineWidget() {
var self = this;

// create the outer wrapper div
this.htmlContent = window.document.createElement("div");
this.$htmlContent = $(this.htmlContent).addClass("inline-widget");
this.$htmlContent.append("<div class='shadow top' />")
.append("<div class='shadow bottom' />");

this.$htmlContent.on("keydown", function (e) {
if (e.keyCode === 27) {
self.close();
e.stopImmediatePropagation();
}
});
}
InlineWidget.prototype.htmlContent = null;
InlineWidget.prototype.$htmlContent = null;
Expand Down
136 changes: 136 additions & 0 deletions src/widgets/PopUpManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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, window */

/**
* Utilities for managing pop-ups.
*/
define(function (require, exports, module) {
"use strict";

var EditorManager = require("editor/EditorManager");

var _popUps = [];

function _removePopUp($popUp, index, visible) {
var initiallyInDOM = $popUp.data("initiallyInDOM"),
removeHandler = $popUp.data("removeHandler");

visible = visible || $popUp.find(":visible").length > 0;

if (removeHandler && visible) {
removeHandler();
}

if (!initiallyInDOM) {
$popUp.remove();
}

_popUps = _popUps.slice(index);
}

/**
* Add Esc key handling for pop-up DOM elements that persist in the DOM.
* These pop-up elements must handle appearance (i.e. opening) themselves.
*
* @param {!jQuery} $popUp jQuery object for the DOM element pop-up
* @param {function} removeHandler Pop-up specific remove (e.g. display:none or DOM removal)
*/
function configurePopUp($popUp, removeHandler) {
_popUps.push($popUp[0]);

$popUp.data("initiallyInDOM", true);
$popUp.data("removeHandler", removeHandler);
}

/**
* Add Esc key handling for transient DOM elements. Immediately adds
* the element to the DOM if it's not already attached.
*
* @param {!jQuery} $popUp jQuery object for the DOM element pop-up
* @param {function} removeHandler Pop-up specific remove (e.g. display:none or DOM removal)
*/
function addPopUp($popUp, removeHandler) {
var initiallyInDOM = $popUp.parent().length === 1;

configurePopUp($popUp, removeHandler, initiallyInDOM);

if (!initiallyInDOM) {
$(window.document.body).append($popUp);
}
}

/**
* Remove Esc key handling for a pop-up. Removes the pop-up from the DOM
* if the pop-up is currently visible and was not originally attached.
*
* @param {!jQuery} $popUp
*/
function removePopUp($popUp) {
var index = _popUps.indexOf($popUp[0]),
initiallyInDOM = $popUp.data("initiallyInDOM"),
removeHandler = $popUp.data("removeHandler");

if (index >= 0) {
_removePopUp($popUp, index);
}
}

function _keydownCaptureListener(keyEvent) {
if (keyEvent.keyCode !== 27) {
return;
}

// allow the popUp to prevent closing
var $popUp,
i,
event = new $.Event("popUpClose");

for (i = _popUps.length - 1; i >= 0; i--) {
$popUp = $(_popUps[i]);

if ($popUp.find(":visible").length > 0) {
$popUp.trigger(event);

if (!event.isDefaultPrevented()) {
// Stop the DOM event from propagating
keyEvent.stopImmediatePropagation();

_removePopUp($popUp, i, true);
EditorManager.focusEditor();
}

break;
}
}
}

window.document.body.addEventListener("keydown", _keydownCaptureListener, true);

exports.addPopUp = addPopUp;
exports.removePopUp = removePopUp;
exports.configurePopUp = configurePopUp;
});

0 comments on commit 57d0b54

Please # to comment.