зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1019676 - Project editor: Allow app header to be updated and add gear icon / status indicator. r=harth
This commit is contained in:
Родитель
da26035b34
Коммит
1bb9dabc3f
|
@ -7,9 +7,9 @@ const promise = require("projecteditor/helpers/promise");
|
|||
const ProjectEditor = require("projecteditor/projecteditor");
|
||||
|
||||
const SAMPLE_PATH = buildTempDirectoryStructure();
|
||||
const SAMPLE_NAME = "DevTools Content";
|
||||
const SAMPLE_NAME = "DevTools Content Application Name";
|
||||
const SAMPLE_PROJECT_URL = "http://mozilla.org";
|
||||
const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-options.svg";
|
||||
const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-debugger.svg";
|
||||
|
||||
/**
|
||||
* Create a workspace for working on projecteditor, available at
|
||||
|
@ -56,11 +56,13 @@ document.addEventListener("DOMContentLoaded", function onDOMReady(e) {
|
|||
projecteditor.setProjectToAppPath(SAMPLE_PATH, {
|
||||
name: SAMPLE_NAME,
|
||||
iconUrl: SAMPLE_ICON,
|
||||
projectOverviewURL: SAMPLE_PROJECT_URL
|
||||
projectOverviewURL: SAMPLE_PROJECT_URL,
|
||||
validationStatus: "valid"
|
||||
}).then(() => {
|
||||
let allResources = projecteditor.project.allResources();
|
||||
console.log("All resources have been loaded", allResources, allResources.map(r=>r.basename).join("|"));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}, false);
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/light-theme.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/projecteditor/projecteditor.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/debugger.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/markup-view.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/markup-view.css" type="text/css"?>
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ const { emit } = require("sdk/event/core");
|
|||
const promise = require("projecteditor/helpers/promise");
|
||||
var { registerPlugin, Plugin } = require("projecteditor/plugins/core");
|
||||
const { AppProjectEditor } = require("./app-project-editor");
|
||||
const OPTION_URL = "chrome://browser/skin/devtools/tool-options.svg";
|
||||
|
||||
var AppManagerRenderer = Class({
|
||||
extends: Plugin,
|
||||
|
@ -25,20 +26,32 @@ var AppManagerRenderer = Class({
|
|||
let {appManagerOpts} = this.host.project;
|
||||
let doc = elt.ownerDocument;
|
||||
let image = doc.createElement("image");
|
||||
let label = doc.createElement("label");
|
||||
let optionImage = doc.createElement("image");
|
||||
let flexElement = doc.createElement("div");
|
||||
let nameLabel = doc.createElement("span");
|
||||
let statusElement = doc.createElement("div");
|
||||
|
||||
label.className = "project-name-label";
|
||||
image.className = "project-image";
|
||||
optionImage.className = "project-options";
|
||||
nameLabel.className = "project-name-label";
|
||||
statusElement.className = "project-status";
|
||||
flexElement.className = "project-flex";
|
||||
|
||||
let name = appManagerOpts.name || resource.basename;
|
||||
let url = appManagerOpts.iconUrl || "icon-sample.png";
|
||||
let status = appManagerOpts.validationStatus || "unknown";
|
||||
|
||||
label.textContent = name;
|
||||
nameLabel.textContent = name;
|
||||
image.setAttribute("src", url);
|
||||
optionImage.setAttribute("src", OPTION_URL);
|
||||
statusElement.setAttribute("status", status)
|
||||
|
||||
elt.innerHTML = "";
|
||||
elt.appendChild(image);
|
||||
elt.appendChild(label);
|
||||
elt.appendChild(nameLabel);
|
||||
elt.appendChild(flexElement);
|
||||
elt.appendChild(statusElement);
|
||||
elt.appendChild(optionImage);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -136,13 +136,11 @@ var Project = Class({
|
|||
/**
|
||||
* Get every file path used inside of the project.
|
||||
*
|
||||
* @returns generator-iterator<string>
|
||||
* @returns Array<string>
|
||||
* A list of all file paths
|
||||
*/
|
||||
allPaths: function*() {
|
||||
for (let [path, store] of this.localStores) {
|
||||
yield path;
|
||||
}
|
||||
allPaths: function() {
|
||||
return [path for (path of this.localStores.keys())];
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -264,14 +264,29 @@ var ProjectEditor = Class({
|
|||
* @param string path
|
||||
* The file path to set
|
||||
* @param Object opts
|
||||
* Custom options used by the project. See plugins/app-manager.
|
||||
* Custom options used by the project.
|
||||
* - name: display name for project
|
||||
* - iconUrl: path to icon for project
|
||||
* - validationStatus: one of 'unknown|error|warning|valid'
|
||||
* - projectOverviewURL: path to load for iframe when project
|
||||
* is selected in the tree.
|
||||
* @param Promise
|
||||
* Promise that is resolved once the project is ready to be used.
|
||||
*/
|
||||
setProjectToAppPath: function(path, opts = {}) {
|
||||
this.project.appManagerOpts = opts;
|
||||
this.project.removeAllStores();
|
||||
this.project.addPath(path);
|
||||
|
||||
let existingPaths = this.project.allPaths();
|
||||
if (existingPaths.length !== 1 || existingPaths[0] !== path) {
|
||||
// Only fully reset if this is a new path.
|
||||
this.project.removeAllStores();
|
||||
this.project.addPath(path);
|
||||
} else {
|
||||
// Otherwise, just ask for the root to be redrawn
|
||||
let rootResource = this.project.localStores.get(path).root;
|
||||
emit(rootResource, "label-change", rootResource);
|
||||
}
|
||||
|
||||
return this.project.refresh();
|
||||
},
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ var ResourceContainer = Class({
|
|||
|
||||
this.line = doc.createElementNS(HTML_NS, "div");
|
||||
this.line.classList.add("child");
|
||||
this.line.classList.add("side-menu-widget-item");
|
||||
this.line.classList.add("entry");
|
||||
this.line.setAttribute("theme", "dark");
|
||||
this.line.setAttribute("tabindex", "0");
|
||||
|
||||
|
@ -223,15 +223,14 @@ var TreeView = Class({
|
|||
this.models = new Set();
|
||||
this.roots = new Set();
|
||||
this._containers = new Map();
|
||||
this.elt = document.createElement("vbox");
|
||||
this.elt = document.createElementNS(HTML_NS, "div");
|
||||
this.elt.tree = this;
|
||||
this.elt.className = "side-menu-widget-container sources-tree";
|
||||
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.setAttribute("flex", "1");
|
||||
this.elt.appendChild(this.children);
|
||||
|
||||
this.resourceChildrenChanged = this.resourceChildrenChanged.bind(this);
|
||||
|
@ -315,7 +314,7 @@ var TreeView = Class({
|
|||
return;
|
||||
}
|
||||
let container = this.importResource(root);
|
||||
container.line.classList.add("side-menu-widget-group-title");
|
||||
container.line.classList.add("entry-group-title");
|
||||
container.line.setAttribute("theme", "dark");
|
||||
this.selectContainer(container);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ support-files =
|
|||
head.js
|
||||
helper_homepage.html
|
||||
|
||||
[browser_projecteditor_app_options.js]
|
||||
[browser_projecteditor_delete_file.js]
|
||||
[browser_projecteditor_editing_01.js]
|
||||
[browser_projecteditor_immediate_destroy.js]
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* 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 options can be changed without resetting the whole
|
||||
// editor.
|
||||
let test = asyncTest(function*() {
|
||||
|
||||
let TEMP_PATH = buildTempDirectoryStructure();
|
||||
let projecteditor = yield addProjectEditorTab();
|
||||
|
||||
let resourceBeenAdded = promise.defer();
|
||||
projecteditor.project.once("resource-added", () => {
|
||||
info ("A resource has been added");
|
||||
resourceBeenAdded.resolve();
|
||||
});
|
||||
|
||||
info ("About to set project to: " + TEMP_PATH);
|
||||
yield projecteditor.setProjectToAppPath(TEMP_PATH, {
|
||||
name: "Test",
|
||||
iconUrl: "chrome://browser/skin/devtools/tool-options.svg",
|
||||
projectOverviewURL: SAMPLE_WEBAPP_URL
|
||||
});
|
||||
|
||||
info ("Making sure a resource has been added before continuing");
|
||||
yield resourceBeenAdded.promise;
|
||||
|
||||
info ("From now on, if a resource is added it should fail");
|
||||
projecteditor.project.on("resource-added", failIfResourceAdded);
|
||||
|
||||
info ("Getting ahold and validating the project header DOM");
|
||||
let header = projecteditor.document.querySelector(".entry-group-title");
|
||||
let image = header.querySelector(".project-image");
|
||||
let nameLabel = header.querySelector(".project-name-label");
|
||||
let statusElement = header.querySelector(".project-status");
|
||||
is (statusElement.getAttribute("status"), "unknown", "The status starts out as unknown.");
|
||||
is (nameLabel.textContent, "Test", "The name label has been set correctly");
|
||||
is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-options.svg", "The icon has been set correctly");
|
||||
|
||||
info ("About to set project with new options.");
|
||||
yield projecteditor.setProjectToAppPath(TEMP_PATH, {
|
||||
name: "Test2",
|
||||
iconUrl: "chrome://browser/skin/devtools/tool-inspector.svg",
|
||||
projectOverviewURL: SAMPLE_WEBAPP_URL,
|
||||
validationStatus: "error"
|
||||
});
|
||||
|
||||
ok (!nameLabel.parentNode, "The old elements have been removed");
|
||||
|
||||
info ("Getting ahold of and validating the project header DOM");
|
||||
let image = header.querySelector(".project-image");
|
||||
let nameLabel = header.querySelector(".project-name-label");
|
||||
let statusElement = header.querySelector(".project-status");
|
||||
is (statusElement.getAttribute("status"), "error", "The status has been set correctly.");
|
||||
is (nameLabel.textContent, "Test2", "The name label has been set correctly");
|
||||
is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-inspector.svg", "The icon has been set correctly");
|
||||
|
||||
info ("About to set project with new options.");
|
||||
yield projecteditor.setProjectToAppPath(TEMP_PATH, {
|
||||
name: "Test3",
|
||||
iconUrl: "chrome://browser/skin/devtools/tool-webconsole.svg",
|
||||
projectOverviewURL: SAMPLE_WEBAPP_URL,
|
||||
validationStatus: "warning"
|
||||
});
|
||||
|
||||
ok (!nameLabel.parentNode, "The old elements have been removed");
|
||||
|
||||
info ("Getting ahold of and validating the project header DOM");
|
||||
let image = header.querySelector(".project-image");
|
||||
let nameLabel = header.querySelector(".project-name-label");
|
||||
let statusElement = header.querySelector(".project-status");
|
||||
is (statusElement.getAttribute("status"), "warning", "The status has been set correctly.");
|
||||
is (nameLabel.textContent, "Test3", "The name label has been set correctly");
|
||||
is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-webconsole.svg", "The icon has been set correctly");
|
||||
|
||||
info ("About to set project with new options.");
|
||||
yield projecteditor.setProjectToAppPath(TEMP_PATH, {
|
||||
name: "Test4",
|
||||
iconUrl: "chrome://browser/skin/devtools/tool-debugger.svg",
|
||||
projectOverviewURL: SAMPLE_WEBAPP_URL,
|
||||
validationStatus: "valid"
|
||||
});
|
||||
|
||||
ok (!nameLabel.parentNode, "The old elements have been removed");
|
||||
|
||||
info ("Getting ahold of and validating the project header DOM");
|
||||
let image = header.querySelector(".project-image");
|
||||
let nameLabel = header.querySelector(".project-name-label");
|
||||
let statusElement = header.querySelector(".project-status");
|
||||
is (statusElement.getAttribute("status"), "valid", "The status has been set correctly.");
|
||||
is (nameLabel.textContent, "Test4", "The name label has been set correctly");
|
||||
is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-debugger.svg", "The icon has been set correctly");
|
||||
|
||||
info ("Test finished, cleaning up");
|
||||
projecteditor.project.off("resource-added", failIfResourceAdded);
|
||||
});
|
||||
|
||||
function failIfResourceAdded() {
|
||||
ok (false, "A resource has been added, but it shouldn't have been");
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
// Test ProjectEditor basic functionality
|
||||
let test = asyncTest(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
let TEMP_PATH = [...projecteditor.project.allPaths()][0];
|
||||
let TEMP_PATH = projecteditor.project.allPaths()[0];
|
||||
|
||||
is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
// Test ProjectEditor basic functionality
|
||||
let test = asyncTest(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
let TEMP_PATH = [...projecteditor.project.allPaths()][0];
|
||||
let TEMP_PATH = projecteditor.project.allPaths()[0];
|
||||
is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
|
||||
|
||||
is ([...projecteditor.project.allPaths()].length, 1, "1 path is set");
|
||||
is (projecteditor.project.allPaths().length, 1, "1 path is set");
|
||||
projecteditor.project.removeAllStores();
|
||||
is ([...projecteditor.project.allPaths()].length, 0, "No paths are remaining");
|
||||
is (projecteditor.project.allPaths().length, 0, "No paths are remaining");
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
let test = asyncTest(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
let TEMP_PATH = [...projecteditor.project.allPaths()][0];
|
||||
let TEMP_PATH = projecteditor.project.allPaths()[0];
|
||||
|
||||
is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
|
||||
|
||||
|
|
|
@ -11,6 +11,20 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
-moz-appearance: treetwisty;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.arrow[open] {
|
||||
-moz-appearance: treetwistyopen;
|
||||
}
|
||||
|
||||
.arrow[invisible] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#projecteditor-menubar {
|
||||
/* XXX: Hide menu bar until we have option to add menu items
|
||||
to an existing one. */
|
||||
|
@ -27,7 +41,13 @@
|
|||
|
||||
.sources-tree {
|
||||
overflow:auto;
|
||||
overflow-x: hidden;
|
||||
-moz-user-focus: normal;
|
||||
|
||||
/* Allows this to expand inside of parent xul element, while
|
||||
still supporting child flexbox elements, including ellipses. */
|
||||
-moz-box-flex: 1;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sources-tree input {
|
||||
|
@ -37,94 +57,95 @@
|
|||
|
||||
#main-deck .sources-tree {
|
||||
background: rgb(225, 225, 225);
|
||||
min-width: 50px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item {
|
||||
.entry {
|
||||
color: #18191A;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .file-label {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
.entry .file-label {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .file-icon {
|
||||
.entry .file-icon {
|
||||
display: inline-block;
|
||||
background: url(file-icons-sheet@2x.png);
|
||||
background-size: 140px 15px;
|
||||
background-repeat: no-repeat;
|
||||
width: 20px;
|
||||
height: 15px;
|
||||
vertical-align: middle;
|
||||
background-position: -40px 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .file-icon.icon-none {
|
||||
.entry .file-icon.icon-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-css {
|
||||
.entry .icon-css {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-js {
|
||||
.entry .icon-js {
|
||||
background-position: -20px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-html {
|
||||
.entry .icon-html {
|
||||
background-position: -40px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-file {
|
||||
.entry .icon-file {
|
||||
background-position: -60px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-folder {
|
||||
.entry .icon-folder {
|
||||
background-position: -80px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-img {
|
||||
.entry .icon-img {
|
||||
background-position: -100px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item .icon-manifest {
|
||||
.entry .icon-manifest {
|
||||
background-position: -120px 0;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item:hover {
|
||||
background: rgba(0, 0, 0, .05);
|
||||
.entry {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
line-height: 20px;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
.entry:hover:not(.entry-group-title):not(.selected) {
|
||||
background: rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-item.selected {
|
||||
background: #3875D7;
|
||||
.entry.selected {
|
||||
background: rgba(56, 117, 215, 1);
|
||||
color: #F5F7FA;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree .side-menu-widget-group-title,
|
||||
#main-deck .sources-tree .side-menu-widget-group-title:hover:not(.selected) {
|
||||
background: #B4D7EB;
|
||||
color: #222;
|
||||
.entry-group-title {
|
||||
background: rgba(56, 117, 215, 0.8);
|
||||
color: #F5F7FA;
|
||||
font-weight: bold;
|
||||
font-size: 1.05em;
|
||||
cursor: default;
|
||||
line-height: 35px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
#main-deck .sources-tree li.child:only-child .side-menu-widget-group-title .expander {
|
||||
.sources-tree .entry-group-title .expander {
|
||||
display: none;
|
||||
}
|
||||
#main-deck .sources-tree .side-menu-widget-item .expander {
|
||||
|
||||
.entry .expander {
|
||||
width: 16px;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -143,30 +164,62 @@
|
|||
padding: 0 3px;
|
||||
}
|
||||
|
||||
/* App Manager */
|
||||
.project-name-label {
|
||||
font-weight: bold;
|
||||
padding-left: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.project-version-label {
|
||||
color: #666;
|
||||
padding-left: 5px;
|
||||
font-size: .9em;
|
||||
.project-flex {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.project-image {
|
||||
max-height: 28px;
|
||||
margin-left: -.5em;
|
||||
vertical-align: middle;
|
||||
max-height: 25px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.editor-image {
|
||||
padding: 10px;
|
||||
.project-image,
|
||||
.project-status,
|
||||
.project-options {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.project-status {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border: solid 1px rgba(255, 255, 255, .5);
|
||||
margin-right: 10px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.project-status[status=valid] {
|
||||
background: #70bf53;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.project-status[status=warning] {
|
||||
background: #d99b28;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.project-status[status=error] {
|
||||
background: #ed2655;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Status Bar */
|
||||
.projecteditor-file-label {
|
||||
font-weight: bold;
|
||||
padding-left: 29px;
|
||||
vertical-align: middle;
|
||||
padding-right: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Image View */
|
||||
.editor-image {
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче