Bug 1019676 - Project editor: Allow app header to be updated and add gear icon / status indicator. r=harth

This commit is contained in:
Brian Grinstead 2014-06-05 13:38:00 -04:00
Родитель da26035b34
Коммит 1bb9dabc3f
12 изменённых файлов: 250 добавлений и 69 удалений

Просмотреть файл

@ -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;
}