Bug 1021827 - Show menu items for project editor inside of App Manager;r=paul

This commit is contained in:
Brian Grinstead 2014-06-26 07:17:54 -05:00
parent bcb978a289
commit 6f7d010a58
23 changed files with 474 additions and 113 deletions

View File

@ -10,7 +10,7 @@ browser.jar:
content/browser/devtools/projecteditor.xul (projecteditor/chrome/content/projecteditor.xul)
content/browser/devtools/readdir.js (projecteditor/lib/helpers/readdir.js)
content/browser/devtools/projecteditor-loader.xul (projecteditor/chrome/content/projecteditor-loader.xul)
content/browser/devtools/projecteditor-test.html (projecteditor/chrome/content/projecteditor-test.html)
content/browser/devtools/projecteditor-test.xul (projecteditor/chrome/content/projecteditor-test.xul)
content/browser/devtools/projecteditor-loader.js (projecteditor/chrome/content/projecteditor-loader.js)
content/browser/devtools/netmonitor.xul (netmonitor/netmonitor.xul)
content/browser/devtools/netmonitor.css (netmonitor/netmonitor.css)

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset='utf-8' />
</head>
<body>
<style type="text/css">
html { height: 100%; }
body {display: flex; padding: 0; margin: 0; min-height: 100%; }
iframe {flex: 1; border: 0;}
</style>
<iframe id='projecteditor-iframe'></iframe>
</body>
</html>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
<commandset id="mainCommandSet">
<commandset id="editMenuCommands"/>
</commandset>
<menubar></menubar>
<iframe id='projecteditor-iframe' flex="1"></iframe>
</window>

View File

@ -46,18 +46,12 @@
<menuitem id="menu_cut"/>
<menuitem id="menu_copy"/>
<menuitem id="menu_paste"/>
<menuseparator/>
<menuitem id="menu_selectAll"/>
<menuseparator/>
<menuitem id="menu_find"/>
<menuitem id="menu_findAgain"/>
</menupopup>
</menu>
</menubar>
<popupset>
<menupopup id="directory-menu-popup">
<menupopup id="context-menu-popup">
</menupopup>
</popupset>

View File

@ -26,6 +26,12 @@ var ItchEditor = Class({
*/
hidesToolbar: false,
/**
* A boolean specifying whether the editor can be edited / saved.
* For instance, a 'save' doesn't make sense on an image.
*/
isEditable: false,
toString: function() {
return this.label || "";
},
@ -35,17 +41,19 @@ var ItchEditor = Class({
},
/**
* Initialize the editor with a single document. This should be called
* Initialize the editor with a single host. This should be called
* by objects extending this object with:
* ItchEditor.prototype.initialize.apply(this, arguments)
*/
initialize: function(document) {
this.doc = document;
initialize: function(host) {
this.doc = host.document;
this.label = "";
this.elt = this.doc.createElement("vbox");
this.elt.setAttribute("flex", "1");
this.elt.editor = this;
this.toolbar = this.doc.querySelector("#projecteditor-toolbar");
this.projectEditorKeyset = host.projectEditorKeyset;
this.projectEditorCommandset = host.projectEditorCommandset;
},
/**
@ -103,6 +111,8 @@ exports.ItchEditor = ItchEditor;
var TextEditor = Class({
extends: ItchEditor,
isEditable: true,
/**
* Extra keyboard shortcuts to use with the editor. Shortcuts defined
* within projecteditor should be triggered when they happen in the editor, and
@ -114,7 +124,7 @@ var TextEditor = Class({
// Copy all of the registered keys into extraKeys object, to notify CodeMirror
// that it should be ignoring these keys
[...this.doc.querySelectorAll("#projecteditor-keyset key")].forEach((key) => {
[...this.projectEditorKeyset.querySelectorAll("key")].forEach((key) => {
let keyUpper = key.getAttribute("key").toUpperCase();
let toolModifiers = key.getAttribute("modifiers");
let modifiers = {
@ -124,9 +134,10 @@ var TextEditor = Class({
// On the key press, we will dispatch the event within projecteditor.
extraKeys[Editor.accel(keyUpper, modifiers)] = () => {
let event = this.doc.createEvent('Event');
let doc = this.projectEditorCommandset.ownerDocument;
let event = doc.createEvent('Event');
event.initEvent('command', true, true);
let command = this.doc.querySelector("#" + key.getAttribute("command"));
let command = this.projectEditorCommandset.querySelector("#" + key.getAttribute("command"));
command.dispatchEvent(event);
};
});
@ -227,22 +238,22 @@ var TextEditor = Class({
/**
* Wrapper for TextEditor using JavaScript syntax highlighting.
*/
function JSEditor(document) {
return TextEditor(document, Editor.modes.js);
function JSEditor(host) {
return TextEditor(host, Editor.modes.js);
}
/**
* Wrapper for TextEditor using CSS syntax highlighting.
*/
function CSSEditor(document) {
return TextEditor(document, Editor.modes.css);
function CSSEditor(host) {
return TextEditor(host, Editor.modes.css);
}
/**
* Wrapper for TextEditor using HTML syntax highlighting.
*/
function HTMLEditor(document) {
return TextEditor(document, Editor.modes.html);
function HTMLEditor(host) {
return TextEditor(host, Editor.modes.html);
}
/**

View File

@ -14,7 +14,7 @@ var AppProjectEditor = Class({
hidesToolbar: true,
initialize: function(document, host) {
initialize: function(host) {
ItchEditor.prototype.initialize.apply(this, arguments);
this.appended = promise.resolve();
this.host = host;

View File

@ -14,11 +14,11 @@ var DeletePlugin = Class({
shouldConfirm: true,
init: function(host) {
this.host.addCommand({
this.host.addCommand(this, {
id: "cmd-delete"
});
this.host.createMenuItem({
parent: "#directory-menu-popup",
parent: this.host.contextMenuPopup,
label: getLocalizedString("projecteditor.deleteLabel"),
command: "cmd-delete"
});

View File

@ -12,7 +12,7 @@ const { ItchEditor } = require("projecteditor/editors");
var ImageEditor = Class({
extends: ItchEditor,
initialize: function(document) {
initialize: function() {
ItchEditor.prototype.initialize.apply(this, arguments);
this.label = "image";
this.appended = promise.resolve();

View File

@ -12,24 +12,23 @@ const { getLocalizedString } = require("projecteditor/helpers/l10n");
var NewFile = Class({
extends: Plugin,
init: function(host) {
this.host.createMenuItem({
parent: "#file-menu-popup",
label: getLocalizedString("projecteditor.newLabel"),
command: "cmd-new",
key: "key-new"
});
this.host.createMenuItem({
parent: "#directory-menu-popup",
label: getLocalizedString("projecteditor.newLabel"),
command: "cmd-new"
});
this.command = this.host.addCommand({
init: function() {
this.command = this.host.addCommand(this, {
id: "cmd-new",
key: getLocalizedString("projecteditor.new.commandkey"),
modifiers: "accel"
});
this.host.createMenuItem({
parent: this.host.fileMenuPopup,
label: getLocalizedString("projecteditor.newLabel"),
command: "cmd-new",
key: "key_cmd-new"
});
this.host.createMenuItem({
parent: this.host.contextMenuPopup,
label: getLocalizedString("projecteditor.newLabel"),
command: "cmd-new"
});
},
onCommand: function(cmd) {

View File

@ -15,29 +15,33 @@ var SavePlugin = Class({
init: function(host) {
this.host.addCommand({
id: "cmd-saveas",
key: getLocalizedString("projecteditor.save.commandkey"),
modifiers: "accel shift"
});
this.host.addCommand({
this.host.addCommand(this, {
id: "cmd-save",
key: getLocalizedString("projecteditor.save.commandkey"),
modifiers: "accel"
});
this.host.addCommand(this, {
id: "cmd-saveas",
key: getLocalizedString("projecteditor.save.commandkey"),
modifiers: "accel shift"
});
this.host.createMenuItem({
parent: this.host.fileMenuPopup,
label: getLocalizedString("projecteditor.saveLabel"),
command: "cmd-save",
key: "key_cmd-save"
});
this.host.createMenuItem({
parent: this.host.fileMenuPopup,
label: getLocalizedString("projecteditor.saveAsLabel"),
command: "cmd-saveas",
key: "key_cmd-saveas"
});
},
// Wait until we can add things into the app manager menu
// this.host.createMenuItem({
// parent: "#file-menu-popup",
// label: "Save",
// command: "cmd-save",
// key: "key-save"
// });
// this.host.createMenuItem({
// parent: "#file-menu-popup",
// label: "Save As",
// command: "cmd-saveas",
// });
isCommandEnabled: function(cmd) {
let currentEditor = this.host.currentEditor;
return currentEditor.isEditable;
},
onCommand: function(cmd) {

View File

@ -76,15 +76,24 @@ var ProjectEditor = Class({
* The iframe to inject the DOM into. If this is not
* specified, then this.load(frame) will need to be called
* before accessing ProjectEditor.
* @param Object options
* - menubar: a <menubar> element to inject menus into
* - menuindex: Integer child index to insert menus
*/
initialize: function(iframe) {
initialize: function(iframe, options = {}) {
this._onTreeSelected = this._onTreeSelected.bind(this);
this._onTreeResourceRemoved = this._onTreeResourceRemoved.bind(this);
this._onEditorCreated = this._onEditorCreated.bind(this);
this._onEditorActivated = this._onEditorActivated.bind(this);
this._onEditorDeactivated = this._onEditorDeactivated.bind(this);
this._updateEditorMenuItems = this._updateEditorMenuItems.bind(this);
this._updateMenuItems = this._updateMenuItems.bind(this);
this.destroy = this.destroy.bind(this);
this.menubar = options.menubar || null;
this.menuindex = options.menuindex || null;
this._menuEnabled = true;
this._destroyed = false;
this._loaded = false;
this._pluginCommands = new Map();
if (iframe) {
this.load(iframe);
}
@ -111,7 +120,12 @@ var ProjectEditor = Class({
this.iframe = iframe;
let domReady = () => {
if (this._destroyed) {
deferred.reject("Error: ProjectEditor has been destroyed before loading");
return;
}
this._onLoad();
this._loaded = true;
deferred.resolve(this);
};
@ -130,9 +144,11 @@ var ProjectEditor = Class({
this.document = this.iframe.contentDocument;
this.window = this.iframe.contentWindow;
this._initCommands();
this._buildMenubar();
this._buildSidebar();
this.window.addEventListener("unload", this.destroy.bind(this));
this.window.addEventListener("unload", this.destroy, false);
// Editor management
this.shells = new ShellDeck(this, this.document);
@ -143,9 +159,6 @@ var ProjectEditor = Class({
let shellContainer = this.document.querySelector("#shells-deck-container");
shellContainer.appendChild(this.shells.elt);
let popup = this.document.querySelector("#edit-menu-popup");
popup.addEventListener("popupshowing", this.updateEditorMenuItems);
// We are not allowing preset projects for now - rebuild a fresh one
// each time.
this.setProject(new Project({
@ -155,10 +168,40 @@ var ProjectEditor = Class({
openFiles: []
}));
this._initCommands();
this._initPlugins();
},
_buildMenubar: function() {
this.editMenu = this.document.getElementById("edit-menu");
this.fileMenu = this.document.getElementById("file-menu");
this.editMenuPopup = this.document.getElementById("edit-menu-popup");
this.fileMenuPopup = this.document.getElementById("file-menu-popup");
this.editMenu.addEventListener("popupshowing", this._updateMenuItems);
this.fileMenu.addEventListener("popupshowing", this._updateMenuItems);
if (this.menubar) {
let body = this.menubar.ownerDocument.body ||
this.menubar.ownerDocument.querySelector("window");
body.appendChild(this.projectEditorCommandset);
body.appendChild(this.projectEditorKeyset);
body.appendChild(this.editorCommandset);
body.appendChild(this.editorKeyset);
body.appendChild(this.contextMenuPopup);
let index = this.menuindex || 0;
this.menubar.insertBefore(this.editMenu, this.menubar.children[index]);
this.menubar.insertBefore(this.fileMenu, this.menubar.children[index]);
} else {
this.document.getElementById("projecteditor-menubar").style.display = "block";
}
// Insert a controller to allow enabling and disabling of menu items.
this._commandWindow = this.editorCommandset.ownerDocument.defaultView;
this._commandController = getCommandController(this);
this._commandWindow.controllers.insertControllerAt(0, this._commandController);
},
/**
* Create the project tree sidebar that lists files.
@ -166,7 +209,8 @@ var ProjectEditor = Class({
_buildSidebar: function() {
this.projectTree = new ProjectTreeView(this.document, {
resourceVisible: this.resourceVisible.bind(this),
resourceFormatter: this.resourceFormatter.bind(this)
resourceFormatter: this.resourceFormatter.bind(this),
contextMenuPopup: this.contextMenuPopup
});
on(this, this.projectTree, "selection", this._onTreeSelected);
on(this, this.projectTree, "resource-removed", this._onTreeResourceRemoved);
@ -179,8 +223,16 @@ var ProjectEditor = Class({
* Set up listeners for commands to dispatch to all of the plugins
*/
_initCommands: function() {
this.commands = this.document.querySelector("#projecteditor-commandset");
this.commands.addEventListener("command", (evt) => {
this.projectEditorCommandset = this.document.getElementById("projecteditor-commandset");
this.projectEditorKeyset = this.document.getElementById("projecteditor-keyset");
this.editorCommandset = this.document.getElementById("editMenuCommands");
this.editorKeyset = this.document.getElementById("editMenuKeys");
this.contextMenuPopup = this.document.getElementById("context-menu-popup");
this.projectEditorCommandset.addEventListener("command", (evt) => {
evt.stopPropagation();
evt.preventDefault();
this.pluginDispatch("onCommand", evt.target.id, evt.target);
@ -207,17 +259,35 @@ var ProjectEditor = Class({
/**
* Enable / disable necessary menu items using globalOverlay.js.
*/
_updateEditorMenuItems: function() {
this.window.goUpdateGlobalEditMenuItems();
this.window.goUpdateGlobalEditMenuItems();
let commands = ['cmd_undo', 'cmd_redo', 'cmd_delete', 'cmd_findAgain'];
commands.forEach(this.window.goUpdateCommand);
_updateMenuItems: function() {
let window = this.editMenu.ownerDocument.defaultView;
let commands = ['cmd_undo', 'cmd_redo', 'cmd_delete', 'cmd_cut', 'cmd_copy', 'cmd_paste'];
commands.forEach(window.goUpdateCommand);
for (let c of this._pluginCommands.keys()) {
window.goUpdateCommand(c);
}
},
/**
* Destroy all objects on the iframe unload event.
*/
destroy: function() {
this._destroyed = true;
// If been destroyed before the iframe finished loading, then
// the properties below will not exist.
if (!this._loaded) {
this.iframe.setAttribute("src", "about:blank");
return;
}
// Reset the src for the iframe so if it reused for a new ProjectEditor
// instance, the load will fire properly.
this.window.removeEventListener("unload", this.destroy, false);
this.iframe.setAttribute("src", "about:blank");
this._plugins.forEach(plugin => { plugin.destroy(); });
forget(this, this.projectTree);
@ -226,6 +296,17 @@ var ProjectEditor = Class({
this.shells.destroy();
this.projectEditorCommandset.remove();
this.projectEditorKeyset.remove();
this.editorCommandset.remove();
this.editorKeyset.remove();
this.contextMenuPopup.remove();
this.editMenu.remove();
this.fileMenu.remove();
this._commandWindow.controllers.removeController(this._commandController);
this._commandController = null;
forget(this, this.project);
this.project.destroy();
this.project = null;
@ -384,11 +465,13 @@ var ProjectEditor = Class({
* @returns DOMElement
* The command element that has been created.
*/
addCommand: function(definition) {
let command = this.document.createElement("command");
addCommand: function(plugin, definition) {
this._pluginCommands.set(definition.id, plugin);
let document = this.projectEditorKeyset.ownerDocument;
let command = document.createElement("command");
command.setAttribute("id", definition.id);
if (definition.key) {
let key = this.document.createElement("key");
let key = document.createElement("key");
key.id = "key_" + definition.id;
let keyName = definition.key;
@ -399,10 +482,10 @@ var ProjectEditor = Class({
}
key.setAttribute("modifiers", definition.modifiers);
key.setAttribute("command", definition.id);
this.document.getElementById("projecteditor-keyset").appendChild(key);
this.projectEditorKeyset.appendChild(key);
}
command.setAttribute("oncommand", "void(0);"); // needed. See bug 371900
this.document.getElementById("projecteditor-commandset").appendChild(command);
this.projectEditorCommandset.appendChild(command);
return command;
},
@ -610,6 +693,49 @@ var ProjectEditor = Class({
get currentEditor() {
return this.shells.currentEditor;
},
/**
* Whether or not menu items should be able to be enabled.
* Note that even if this is true, certain menu items will not be
* enabled until the correct state is achieved (for instance, the
* 'copy' menu item is only enabled when there is a selection).
* But if this is false, then nothing will be enabled.
*/
set menuEnabled(val) {
this._menuEnabled = val;
this._updateMenuItems();
},
get menuEnabled() {
return this._menuEnabled;
}
});
/**
* Returns a controller object that can be used for
* editor-specific commands such as find, jump to line,
* copy/paste, etc.
*/
function getCommandController(host) {
return {
supportsCommand: function (cmd) {
return host._pluginCommands.get(cmd);
},
isCommandEnabled: function (cmd) {
if (!host.menuEnabled) {
return false;
}
let plugin = host._pluginCommands.get(cmd);
if (plugin && plugin.isCommandEnabled) {
return plugin.isCommandEnabled(cmd);
}
return true;
},
doCommand: function(cmd) {
}
};
}
exports.ProjectEditor = ProjectEditor;

View File

@ -36,7 +36,7 @@ var Shell = Class({
let constructor = this._editorTypeForResource();
this.editor = constructor(this.doc, this.host);
this.editor = constructor(this.host);
this.editor.shell = this;
this.editorAppended = this.editor.appended;

View File

@ -106,7 +106,7 @@ var ResourceContainer = Class({
*/
openContextMenu: function(ev) {
ev.preventDefault();
let popup = this.tree.doc.getElementById("directory-menu-popup");
let popup = this.tree.options.contextMenuPopup;
popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
},
@ -208,13 +208,14 @@ var TreeView = Class({
/**
* @param Document document
* @param Object options
* - contextMenuPopup: a <menupopup> element
* - resourceFormatter: a function(Resource, DOMNode)
* that renders the resource into the view
* - resourceVisible: a function(Resource) -> Boolean
* that determines if the resource should show up.
*/
initialize: function(document, options) {
this.doc = document;
initialize: function(doc, options) {
this.doc = doc;
this.options = merge({
resourceFormatter: function(resource, elt) {
elt.textContent = resource.toString();
@ -223,14 +224,14 @@ var TreeView = Class({
this.models = new Set();
this.roots = new Set();
this._containers = new Map();
this.elt = document.createElementNS(HTML_NS, "div");
this.elt = this.doc.createElementNS(HTML_NS, "div");
this.elt.tree = this;
this.elt.className = "sources-tree";
this.elt.setAttribute("with-arrows", "true");
this.elt.setAttribute("theme", "dark");
this.elt.setAttribute("flex", "1");
this.children = document.createElementNS(HTML_NS, "ul");
this.children = this.doc.createElementNS(HTML_NS, "ul");
this.elt.appendChild(this.children);
this.resourceChildrenChanged = this.resourceChildrenChanged.bind(this);

View File

@ -12,6 +12,8 @@ support-files =
[browser_projecteditor_external_change.js]
[browser_projecteditor_immediate_destroy.js]
[browser_projecteditor_init.js]
[browser_projecteditor_menubar_01.js]
[browser_projecteditor_menubar_02.js]
[browser_projecteditor_new_file.js]
[browser_projecteditor_stores.js]
[browser_projecteditor_tree_selection.js]

View File

@ -37,7 +37,7 @@ let test = asyncTest(function*() {
let defer = promise.defer();
let resource = container.resource;
let popup = projecteditor.document.getElementById("directory-menu-popup");
let popup = projecteditor.contextMenuPopup;
info ("Going to attempt deletion for: " + resource.path)
onPopupShow(popup).then(function () {

View File

@ -10,8 +10,9 @@
let test = asyncTest(function* () {
info ("Testing tab closure when projecteditor is in various states");
let loaderUrl = "chrome://browser/content/devtools/projecteditor-test.xul";
yield addTab("chrome://browser/content/devtools/projecteditor-test.html").then(() => {
yield addTab(loaderUrl).then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
ok (iframe, "Tab has placeholder iframe for projecteditor");
@ -19,7 +20,7 @@ let test = asyncTest(function* () {
gBrowser.removeCurrentTab();
});
yield addTab("chrome://browser/content/devtools/projecteditor-test.html").then(() => {
yield addTab(loaderUrl).then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
ok (iframe, "Tab has placeholder iframe for projecteditor");
@ -30,7 +31,7 @@ let test = asyncTest(function* () {
gBrowser.removeCurrentTab();
});
yield addTab("chrome://browser/content/devtools/projecteditor-test.html").then(() => {
yield addTab(loaderUrl).then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
ok (iframe, "Tab has placeholder iframe for projecteditor");
@ -43,7 +44,7 @@ let test = asyncTest(function* () {
gBrowser.removeCurrentTab();
});
yield addTab("chrome://browser/content/devtools/projecteditor-test.html").then(() => {
yield addTab(loaderUrl).then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
ok (iframe, "Tab has placeholder iframe for projecteditor");
@ -56,6 +57,29 @@ let test = asyncTest(function* () {
});
});
yield addTab(loaderUrl).then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
ok (iframe, "Tab has placeholder iframe for projecteditor");
let projecteditor = ProjectEditor.ProjectEditor(iframe);
ok (projecteditor, "ProjectEditor has been initialized");
let loadedDone = promise.defer();
projecteditor.loaded.then(() => {
ok (false, "Loaded has finished after destroy() has been called");
loadedDone.resolve();
}, () => {
ok (true, "Loaded has been rejected after destroy() has been called");
loadedDone.resolve();
});
projecteditor.destroy();
return loadedDone.promise.then(() => {
gBrowser.removeCurrentTab();
});
});
finish();
});

View File

@ -0,0 +1,28 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that menu bar appends to the correct document.
let test = asyncTest(function*() {
let projecteditor = yield addProjectEditorTabForTempDirectory({
menubar: false
});
ok(projecteditor, "ProjectEditor has loaded");
let fileMenu = projecteditor.document.getElementById("file-menu");
let editMenu = projecteditor.document.getElementById("edit-menu");
ok (fileMenu, "The menu has loaded in the projecteditor document");
ok (editMenu, "The menu has loaded in the projecteditor document");
let projecteditor2 = yield addProjectEditorTabForTempDirectory();
let menubar = projecteditor2.menubar;
let fileMenu = projecteditor2.document.getElementById("file-menu");
let editMenu = projecteditor2.document.getElementById("edit-menu");
ok (!fileMenu, "The menu has NOT loaded in the projecteditor document");
ok (!editMenu, "The menu has NOT loaded in the projecteditor document");
ok (content.document.querySelector("#file-menu"), "The menu has loaded in the specified element");
ok (content.document.querySelector("#edit-menu"), "The menu has loaded in the specified element");
});

View File

@ -0,0 +1,126 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
loadHelperScript("helper_edits.js");
// Test menu bar enabled / disabled state.
let test = asyncTest(function*() {
let projecteditor = yield addProjectEditorTabForTempDirectory();
let menubar = projecteditor.menubar;
// let projecteditor = yield addProjectEditorTabForTempDirectory();
ok(projecteditor, "ProjectEditor has loaded");
let fileMenu = menubar.querySelector("#file-menu");
let editMenu = menubar.querySelector("#edit-menu");
ok (fileMenu, "The menu has loaded in the projecteditor document");
ok (editMenu, "The menu has loaded in the projecteditor document");
let cmdNew = fileMenu.querySelector("[command=cmd-new]");
let cmdSave = fileMenu.querySelector("[command=cmd-save]");
let cmdSaveas = fileMenu.querySelector("[command=cmd-saveas]");
let cmdUndo = editMenu.querySelector("[command=cmd_undo]");
let cmdRedo = editMenu.querySelector("[command=cmd_redo]");
let cmdCut = editMenu.querySelector("[command=cmd_cut]");
let cmdCopy = editMenu.querySelector("[command=cmd_copy]");
let cmdPaste = editMenu.querySelector("[command=cmd_paste]");
info ("Checking initial state of menus");
yield openAndCloseMenu(fileMenu);
yield openAndCloseMenu(editMenu);
is (cmdNew.getAttribute("disabled"), "", "File menu item is enabled");
is (cmdSave.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdSaveas.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdUndo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdRedo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCut.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCopy.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdPaste.getAttribute("disabled"), "true", "Edit menu item is disabled");
projecteditor.menuEnabled = false;
info ("Checking with menuEnabled = false");
yield openAndCloseMenu(fileMenu);
yield openAndCloseMenu(editMenu);
is (cmdNew.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdSave.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdSaveas.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdUndo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdRedo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCut.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCopy.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdPaste.getAttribute("disabled"), "true", "Edit menu item is disabled");
info ("Checking with menuEnabled=true");
projecteditor.menuEnabled = true;
yield openAndCloseMenu(fileMenu);
yield openAndCloseMenu(editMenu);
is (cmdNew.getAttribute("disabled"), "", "File menu item is enabled");
is (cmdSave.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdSaveas.getAttribute("disabled"), "true", "File menu item is disabled");
is (cmdUndo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdRedo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCut.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCopy.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdPaste.getAttribute("disabled"), "true", "Edit menu item is disabled");
info ("Checking with resource selected");
let resource = projecteditor.project.allResources()[2];
yield selectFile(projecteditor, resource);
let editor = projecteditor.currentEditor;
editor.editor.focus();
EventUtils.synthesizeKey("foo", { }, projecteditor.window);
yield openAndCloseMenu(fileMenu);
yield openAndCloseMenu(editMenu);
is (cmdNew.getAttribute("disabled"), "", "File menu item is enabled");
is (cmdSave.getAttribute("disabled"), "", "File menu item is enabled");
is (cmdSaveas.getAttribute("disabled"), "", "File menu item is enabled");
is (cmdUndo.getAttribute("disabled"), "", "Edit menu item is enabled");
is (cmdRedo.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCut.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdCopy.getAttribute("disabled"), "true", "Edit menu item is disabled");
is (cmdPaste.getAttribute("disabled"), "", "Edit menu item is enabled");
});
function openAndCloseMenu(menu) {
let shown = onPopupShow(menu)
EventUtils.synthesizeMouseAtCenter(menu, {}, menu.ownerDocument.defaultView);
yield shown;
let hidden = onPopupHidden(menu)
EventUtils.synthesizeMouseAtCenter(menu, {}, menu.ownerDocument.defaultView);
yield hidden;
}
function onPopupShow(menu) {
let defer = promise.defer();
menu.addEventListener("popupshown", function onpopupshown() {
menu.removeEventListener("popupshown", onpopupshown);
defer.resolve();
});
return defer.promise;
}
function onPopupHidden(menu) {
let defer = promise.defer();
menu.addEventListener("popuphidden", function onpopupshown() {
menu.removeEventListener("popuphidden", onpopupshown);
defer.resolve();
});
return defer.promise;
}

View File

@ -88,25 +88,29 @@ function loadHelperScript(filePath) {
Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
}
function addProjectEditorTabForTempDirectory() {
function addProjectEditorTabForTempDirectory(opts = {}) {
TEMP_PATH = buildTempDirectoryStructure();
let CUSTOM_OPTS = {
let customOpts = {
name: "Test",
iconUrl: "chrome://browser/skin/devtools/tool-options.svg",
projectOverviewURL: SAMPLE_WEBAPP_URL
};
return addProjectEditorTab().then((projecteditor) => {
return projecteditor.setProjectToAppPath(TEMP_PATH, CUSTOM_OPTS).then(() => {
return addProjectEditorTab(opts).then((projecteditor) => {
return projecteditor.setProjectToAppPath(TEMP_PATH, customOpts).then(() => {
return projecteditor;
});
});
}
function addProjectEditorTab() {
return addTab("chrome://browser/content/devtools/projecteditor-test.html").then(() => {
function addProjectEditorTab(opts = {}) {
return addTab("chrome://browser/content/devtools/projecteditor-test.xul").then(() => {
let iframe = content.document.getElementById("projecteditor-iframe");
let projecteditor = ProjectEditor.ProjectEditor(iframe);
if (opts.menubar !== false) {
opts.menubar = content.document.querySelector("menubar");
}
let projecteditor = ProjectEditor.ProjectEditor(iframe, opts);
ok (iframe, "Tab has placeholder iframe for projecteditor");
ok (projecteditor, "ProjectEditor has been initialized");

View File

@ -321,13 +321,34 @@ let UI = {
// ProjectEditor & details screen
destroyProjectEditor: function() {
if (this.projecteditor) {
this.projecteditor.destroy();
this.projecteditor = null;
}
},
updateProjectEditorMenusVisibility: function() {
if (this.projecteditor) {
let panel = document.querySelector("#deck").selectedPanel;
if (panel && panel.id == "deck-panel-projecteditor") {
this.projecteditor.menuEnabled = true;
} else {
this.projecteditor.menuEnabled = false;
}
}
},
getProjectEditor: function() {
if (this.projecteditor) {
return this.projecteditor.loaded;
}
let projecteditorIframe = document.querySelector("#deck-panel-projecteditor");
this.projecteditor = ProjectEditor.ProjectEditor(projecteditorIframe);
this.projecteditor = ProjectEditor.ProjectEditor(projecteditorIframe, {
menubar: document.querySelector("#main-menubar"),
menuindex: 1
});
this.projecteditor.on("onEditorSave", (editor, resource) => {
AppManager.validateProject(AppManager.selectedProject);
});
@ -409,12 +430,14 @@ let UI = {
let deck = document.querySelector("#deck");
let panel = deck.querySelector("#deck-panel-" + id);
deck.selectedPanel = panel;
this.updateProjectEditorMenusVisibility();
},
resetDeck: function() {
this.resetFocus();
let deck = document.querySelector("#deck");
deck.selectedPanel = null;
this.updateProjectEditorMenusVisibility();
},
/********** COMMANDS **********/
@ -822,7 +845,11 @@ let Cmds = {
},
toggleEditors: function() {
Services.prefs.setBoolPref("devtools.webide.showProjectEditor", !UI.isProjectEditorEnabled());
let isNowEnabled = !UI.isProjectEditorEnabled();
Services.prefs.setBoolPref("devtools.webide.showProjectEditor", isNowEnabled);
if (!isNowEnabled) {
UI.destroyProjectEditor();
}
UI.openProject();
},

View File

@ -9,6 +9,8 @@
%webideDTD;
]>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
@ -23,6 +25,7 @@
width="640" height="480"
persist="screenX screenY width height">
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
<script type="application/javascript" src="webide.js"></script>
<script type="application/javascript" src="cli.js"></script>

View File

@ -32,10 +32,19 @@ projecteditor.deleteFolderPromptMessage=Are you sure you want to delete this fol
projecteditor.deleteFilePromptMessage=Are you sure you want to delete this file?
# LOCALIZATION NOTE (projecteditor.newLabel):
# This string is displayed as a context menu item for adding a new file to
# This string is displayed as a menu item for adding a new file to
# the directory.
projecteditor.newLabel=New…
# LOCALIZATION NOTE (projecteditor.saveLabel):
# This string is displayed as a menu item for saving the current file.
projecteditor.saveLabel=Save
# LOCALIZATION NOTE (projecteditor.saveAsLabel):
# This string is displayed as a menu item for saving the current file
# with a new name.
projecteditor.saveAsLabel=Save As…
# LOCALIZATION NOTE (projecteditor.selectFileLabel):
# This string is displayed as the title on the file picker when saving a file.
projecteditor.selectFileLabel=Select a File

View File

@ -26,8 +26,6 @@
}
#projecteditor-menubar {
/* XXX: Hide menu bar until we have option to add menu items
to an existing one. */
display: none;
}