diff --git a/src/editor/EditorManager.js b/src/editor/EditorManager.js index 8f3200d4287..06dcf721e9c 100644 --- a/src/editor/EditorManager.js +++ b/src/editor/EditorManager.js @@ -52,6 +52,7 @@ define(function (require, exports, module) { // Load dependent modules var Commands = require("command/Commands"), + PanelManager = require("view/PanelManager"), CommandManager = require("command/CommandManager"), DocumentManager = require("document/DocumentManager"), PerfUtils = require("utils/PerfUtils"), @@ -341,55 +342,43 @@ define(function (require, exports, module) { /** - * Calculates the available height for the full-size Editor (or the no-editor placeholder), - * accounting for the current size of all visible panels, toolbar, & status bar. - * @return {number} - */ - function _calcEditorHeight() { - var availableHt = $(".content").height(); - - _editorHolder.siblings().each(function (i, elem) { - var $elem = $(elem); - if ($elem.css("display") !== "none") { - availableHt -= $elem.outerHeight(); - } - }); - - // Clip value to 0 (it could be negative if a panel wants more space than we have) - return Math.max(availableHt, 0); - } - - /** - * Flag for resizeEditor() to always force refresh. + * Flag for _onEditorAreaResize() to always force refresh. * @const * @type {string} */ var REFRESH_FORCE = "force"; /** - * Flag for resizeEditor() to never refresh. + * Flag for _onEditorAreaResize() to never refresh. * @const * @type {string} */ var REFRESH_SKIP = "skip"; - /** - * Resize the editor. This must be called any time the contents of the editor area are swapped - * or any time the editor area might change height. EditorManager takes care of calling this when - * the Editor is swapped, and on window resize. But anyone who changes size/visiblity of editor - * area siblings (toolbar, status bar, bottom panels) *must* manually call resizeEditor(). - * - * @param {string=} refreshFlag For internal use. Set to "force" to ensure the editor will refresh, - * "skip" to ensure the editor does not refresh, or leave undefined to let resizeEditor() determine - * whether it needs to refresh. + /** + * Must be called whenever the size/visibility of editor area siblings is changed without going through + * PanelManager or Resizer. Resizable panels created via PanelManager do not require this manual call. */ - function resizeEditor(refreshFlag) { + function resizeEditor() { if (!_editorHolder) { return; // still too early during init } - - var editorAreaHt = _calcEditorHeight(); - _editorHolder.height(editorAreaHt); // affects size of "not-editor" placeholder as well + // PanelManager computes the correct editor-holder size & calls us back with it, via _onEditorAreaResize() + PanelManager._notifyLayoutChange(); + } + + /** + * Update the current CodeMirror editor's size. Must be called any time the contents of the editor area + * are swapped or any time the editor-holder area has changed height. EditorManager calls us in the swap + * case. PanelManager calls us in the most common height-change cases (panel and/or window resize), but + * some other cases are handled by external code calling resizeEditor() (e.g. ModalBar hide/show). + * + * @param {number} editorAreaHt + * @param {string=} refreshFlag For internal use. Set to "force" to ensure the editor will refresh, + * "skip" to ensure the editor does not refresh, or leave undefined to let _onEditorAreaResize() + * determine whether it needs to refresh. + */ + function _onEditorAreaResize(event, editorAreaHt, refreshFlag) { if (_currentEditor) { var curRoot = _currentEditor.getRootElement(), @@ -412,15 +401,6 @@ define(function (require, exports, module) { } } - /** - * NJ's editor-resizing fix. Whenever the window resizes, we immediately adjust the editor's - * height. - */ - function _updateEditorDuringResize() { - // always skip the refresh since CodeMirror will call refresh() itself when it sees the resize event - resizeEditor(REFRESH_SKIP); - } - /** Updates _viewStateCache from the given editor's actual current state */ function _saveEditorViewState(editor) { @@ -465,13 +445,13 @@ define(function (require, exports, module) { _currentEditorsDocument = document; _currentEditor = document._masterEditor; - // Skip refreshing the editor since we're going to refresh it in resizeEditor() later. + // Skip refreshing the editor since we're going to refresh it more explicitly below _currentEditor.setVisible(true, false); _currentEditor.focus(); // Resize and refresh the editor, since it might have changed size or had other edits applied // since it was last visible. - resizeEditor(REFRESH_FORCE); + PanelManager._notifyLayoutChange(REFRESH_FORCE); } /** @@ -705,11 +685,8 @@ define(function (require, exports, module) { $(DocumentManager).on("currentDocumentChange", _onCurrentDocumentChange); $(DocumentManager).on("workingSetRemove", _onWorkingSetRemove); $(DocumentManager).on("workingSetRemoveList", _onWorkingSetRemoveList); + $(PanelManager).on("editorAreaResize", _onEditorAreaResize); - // Add this as a capture handler so we're guaranteed to run it before the editor does its own - // refresh on resize. - window.addEventListener("resize", _updateEditorDuringResize, true); - // For unit tests and internal use only exports._openInlineWidget = _openInlineWidget; exports._createFullEditorForDocument = _createFullEditorForDocument; diff --git a/src/extensions/default/JSLint/main.js b/src/extensions/default/JSLint/main.js index 89fa889d0aa..ae2b96ccadd 100644 --- a/src/extensions/default/JSLint/main.js +++ b/src/extensions/default/JSLint/main.js @@ -36,6 +36,7 @@ define(function (require, exports, module) { // Load dependent modules var Commands = brackets.getModule("command/Commands"), + PanelManager = brackets.getModule("view/PanelManager"), CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"), DocumentManager = brackets.getModule("document/DocumentManager"), @@ -156,7 +157,7 @@ define(function (require, exports, module) { EditorManager.focusEditor(); }); - $lintResults.show(); + Resizer.show($lintResults); if (JSLINT.errors.length === 1) { StatusBar.updateIndicator(INDICATOR_ID, true, "jslint-errors", Strings.JSLINT_ERROR_INFORMATION); } else { @@ -175,7 +176,7 @@ define(function (require, exports, module) { setGotoEnabled(true); } else { - $lintResults.hide(); + Resizer.hide($lintResults); StatusBar.updateIndicator(INDICATOR_ID, true, "jslint-valid", Strings.JSLINT_NO_ERRORS); setGotoEnabled(false); } @@ -184,12 +185,10 @@ define(function (require, exports, module) { } else { // JSLint is disabled or does not apply to the current file, hide the results - $lintResults.hide(); + Resizer.hide($lintResults); StatusBar.updateIndicator(INDICATOR_ID, true, "jslint-disabled", Strings.JSLINT_DISABLED); setGotoEnabled(false); } - - EditorManager.resizeEditor(); } /** @@ -263,20 +262,15 @@ define(function (require, exports, module) { ExtensionUtils.loadStyleSheet(module, "jslint.css"); var jsLintHtml = Mustache.render(JSLintTemplate, Strings); - $(jsLintHtml).insertBefore("#status-bar"); + var resultsPanel = PanelManager.createBottomPanel("jslint.results", $(jsLintHtml), 100); + $lintResults = $("#jslint-results"); var goldStarHtml = Mustache.render("
", Strings); $(goldStarHtml).insertBefore("#status-file"); - $lintResults = $("#jslint-results"); - StatusBar.addIndicator(INDICATOR_ID, $("#gold-star")); // Called on HTML ready to trigger the initial UI state setEnabled(_prefs.getValue("enabled")); - - // AppInit.htmlReady() has already executed before extensions are loaded - // so, for now, we need to call this ourself - Resizer.makeResizable($lintResults.get(0), "vert", "top", 100); }); }); diff --git a/src/extensions/default/JavaScriptCodeHints/unittests.js b/src/extensions/default/JavaScriptCodeHints/unittests.js index 9c0599cde7b..8bf4e50963a 100644 --- a/src/extensions/default/JavaScriptCodeHints/unittests.js +++ b/src/extensions/default/JavaScriptCodeHints/unittests.js @@ -49,26 +49,15 @@ define(function (require, exports, module) { DocumentManager.setCurrentDocument(doc); }); }); + /** - * Returns an Editor suitable for use in isolation, given a Document. (Unlike - * SpecRunnerUtils.createMockEditor(), which is given text and creates the Document - * for you). + * Returns an Editor suitable for use in isolation, given a Document. * * @param {Document} doc - the document to be contained by the new Editor * @return {Editor} - the mock editor object */ function createMockEditor(doc) { - // Initialize EditorManager - var $editorHolder = $("
"); - EditorManager.setEditorHolder($editorHolder); - $("body").append($editorHolder); - - // create Editor instance - var editor = new Editor(doc, true, $editorHolder.get(0)); - - EditorManager._notifyActiveEditorChanged(editor); - - return editor; + return SpecRunnerUtils.createMockEditorForDocument(doc); } describe("JavaScript Code Hinting", function () { diff --git a/src/htmlContent/main-view.html b/src/htmlContent/main-view.html index fff3db4d42a..a66abe19c62 100644 --- a/src/htmlContent/main-view.html +++ b/src/htmlContent/main-view.html @@ -62,7 +62,7 @@ Note: all children must be in a vertical stack with heights explicitly set in a fixed unit such as px (not percent/em/auto). If you change the height later, you must call EditorManager.resizeEditor() each time. Otherwise editor-holder's height will - not get set correctly. + not get set correctly. Use PanelManager to have this managed for you automatically. -->
@@ -83,14 +83,7 @@
-
-
-
{{SEARCH_RESULTS}}
-
- × -
-
-
+ diff --git a/src/htmlContent/search-results.html b/src/htmlContent/search-results.html new file mode 100644 index 00000000000..e46d823f469 --- /dev/null +++ b/src/htmlContent/search-results.html @@ -0,0 +1,8 @@ +
+
+
{{SEARCH_RESULTS}}
+
+ × +
+
+
diff --git a/src/search/FindInFiles.js b/src/search/FindInFiles.js index 756cfc6805f..832dbd01197 100644 --- a/src/search/FindInFiles.js +++ b/src/search/FindInFiles.js @@ -22,7 +22,7 @@ */ /*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ -/*global define, $, PathUtils, window */ +/*global define, $, PathUtils, window, Mustache */ /* * Adds a "find in files" command to allow the user to find all occurances of a string in all files in @@ -43,6 +43,7 @@ define(function (require, exports, module) { "use strict"; var Async = require("utils/Async"), + Resizer = require("utils/Resizer"), CommandManager = require("command/CommandManager"), Commands = require("command/Commands"), Strings = require("strings"), @@ -50,12 +51,15 @@ define(function (require, exports, module) { ProjectManager = require("project/ProjectManager"), DocumentManager = require("document/DocumentManager"), EditorManager = require("editor/EditorManager"), + PanelManager = require("view/PanelManager"), FileIndexManager = require("project/FileIndexManager"), FileUtils = require("file/FileUtils"), KeyEvent = require("utils/KeyEvent"), AppInit = require("utils/AppInit"), StatusBar = require("widgets/StatusBar"), ModalBar = require("widgets/ModalBar").ModalBar; + + var searchResultsTemplate = require("text!htmlContent/search-results.html"); var searchResults = []; @@ -64,8 +68,8 @@ define(function (require, exports, module) { currentQuery = "", currentScope; - // Div holding the search results. Initialized in htmlReady(). - var $searchResultsDiv; + // Bottom panel holding the search results. Initialized in htmlReady(). + var searchResultsPanel; function _getQueryRegExp(query) { // Clear any pending RegEx error message @@ -193,9 +197,8 @@ define(function (require, exports, module) { }; function _hideSearchResults() { - if ($searchResultsDiv.is(":visible")) { - $searchResultsDiv.hide(); - EditorManager.resizeEditor(); + if (searchResultsPanel.isVisible()) { + searchResultsPanel.hide(); } } @@ -340,12 +343,10 @@ define(function (require, exports, module) { _hideSearchResults(); }); - $searchResultsDiv.show(); + searchResultsPanel.show(); } else { _hideSearchResults(); } - - EditorManager.resizeEditor(); } /** @@ -444,11 +445,12 @@ define(function (require, exports, module) { // Initialize items dependent on HTML DOM AppInit.htmlReady(function () { - $searchResultsDiv = $("#search-results"); + var panelHtml = Mustache.render(searchResultsTemplate, Strings); + searchResultsPanel = PanelManager.createBottomPanel("find-in-files.results", $(panelHtml)); }); function _fileNameChangeHandler(event, oldName, newName) { - if ($searchResultsDiv.is(":visible")) { + if (searchResultsPanel.isVisible()) { // Update the search results searchResults.forEach(function (item) { item.fullPath = item.fullPath.replace(oldName, newName); @@ -458,7 +460,7 @@ define(function (require, exports, module) { } function _pathDeletedHandler(event, path) { - if ($searchResultsDiv.is(":visible")) { + if (searchResultsPanel.isVisible()) { // Update the search results searchResults.forEach(function (item, idx) { if (FileUtils.isAffectedWhenRenaming(item.fullPath, path)) { diff --git a/src/utils/Resizer.js b/src/utils/Resizer.js index f8d967cd2b4..c97f7d38748 100644 --- a/src/utils/Resizer.js +++ b/src/utils/Resizer.js @@ -60,8 +60,7 @@ define(function (require, exports, module) { // Load dependent modules var AppInit = require("utils/AppInit"), - PreferencesManager = require("preferences/PreferencesManager"), - EditorManager = require("editor/EditorManager"); + PreferencesManager = require("preferences/PreferencesManager"); /** * @private @@ -139,17 +138,20 @@ define(function (require, exports, module) { * @param {!string} direction Direction of the resize action: one of the DIRECTION_* constants. * @param {!string} position Which side of the element can be dragged: one of the POSITION_* constants * (TOP/BOTTOM for vertical resizing or LEFT/RIGHT for horizontal). - * @param {?number} minSize Minimum size (width or height) of the element. Defaults to 0. + * @param {?number} minSize Minimum size (width or height) of the element's outer dimensions, including + * border & padding. Defaults to 0. * @param {?boolean} collapsible Indicates the panel is collapsible on double click on the * resizer. Defaults to false. * @param {?string} forceLeft CSS selector indicating element whose 'left' should be locked to the * the resizable element's size (useful for siblings laid out to the right of * the element). Must lie in element's parent's subtree. + * @param {?boolean} createdByPanelManager For internal use only */ - function makeResizable(element, direction, position, minSize, collapsible, forceLeft) { + function makeResizable(element, direction, position, minSize, collapsible, forceLeft, createdByPanelManager) { var $resizer = $('
'), $element = $(element), + $parent = $element.parent(), $resizableElement = $($element.find(".resizable-content:first")[0]), $body = $(window.document.body), elementID = $element.attr("id"), @@ -166,9 +168,18 @@ define(function (require, exports, module) { $element.prepend($resizer); + // Important so min/max sizes behave predictably + $element.css("box-sizing", "border-box"); + + // Detect legacy cases where panels in the editor area are created without using PanelManager APIs + if ($parent[0] && $parent.is(".content") && !createdByPanelManager) { + console.warn("Deprecated: resizable panels should be created via PanelManager.createBottomPanel(). Using Resizer directly will stop working in the future. \nElement:", element); + $(exports).triggerHandler("deprecatedPanelAdded", [$element]); + } + function adjustSibling(size) { if (forceLeft !== undefined) { - $(forceLeft, $element.parent()).css("left", size); + $(forceLeft, $parent).css("left", size); } } @@ -205,9 +216,6 @@ define(function (require, exports, module) { adjustSibling(elementSize); - // Vertical resize affects editor directly; horizontal resize could change height of top toolbar - EditorManager.resizeEditor(); - $element.trigger("panelExpanded", [elementSize]); _prefs.setValue(elementID, elementPrefs); }); @@ -230,9 +238,6 @@ define(function (require, exports, module) { adjustSibling(0); - // Vertical resize affects editor directly; horizontal resize could change height of top toolbar - EditorManager.resizeEditor(); - $element.trigger("panelCollapsed", [elementSize]); _prefs.setValue(elementID, elementPrefs); }); @@ -307,9 +312,6 @@ define(function (require, exports, module) { $element.trigger("panelResizeStart", newSize); } } - - // Vertical resize affects editor directly; horizontal resize could change height of top toolbar - EditorManager.resizeEditor(); } animationRequest = window.webkitRequestAnimationFrame(doRedraw); @@ -319,6 +321,13 @@ define(function (require, exports, module) { // calculate newSize adding to startSize the difference // between starting and current position, capped at minSize newSize = Math.max(startSize + directionIncrement * (startPosition - e[directionProperty]), minSize); + + // respect max size if one provided (e.g. by PanelManager) + var maxSize = $element.data("maxsize"); + if (maxSize !== undefined) { + newSize = Math.min(newSize, maxSize); + } + e.preventDefault(); if (animationRequest === null) { diff --git a/src/view/PanelManager.js b/src/view/PanelManager.js new file mode 100644 index 00000000000..28f39c5d806 --- /dev/null +++ b/src/view/PanelManager.js @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2013 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, window, $, brackets */ + +/** + * Manages layout of panels surrounding the editor area, and size of the editor area (but not its contents). + * + * Updates panel sizes when the window is resized. Maintains the max resizing limits for panels, based on + * currently available window size. + * + * Events: + * - editorAreaResize -- When editor-holder's size changes for any reason (including panel show/hide + * panel resize, or the window resize). + * The 2nd arg is the new editor-holder height. + * The 3rd arg is a refreshHint flag for internal EditorManager use. + */ +define(function (require, exports, module) { + "use strict"; + + var AppInit = require("utils/AppInit"), + Resizer = require("utils/Resizer"); + + + /** @type {jQueryObject} The ".content" vertical stack (editor + all header/footer panels) */ + var $windowContent; + + /** @type {jQueryObject} The "#editor-holder": has only one visible child, the current CodeMirror + instance (or the no-editor placeholder) */ + var $editorHolder; + + /** @type {boolean} Have we already started listening for the end of the ongoing window resize? */ + var windowResizing = false; + + + /** + * Calculates the available height for the full-size Editor (or the no-editor placeholder), + * accounting for the current size of all visible panels, toolbar, & status bar. + * @return {number} + */ + function calcEditorHeight() { + var availableHt = $windowContent.height(); + + $editorHolder.siblings().each(function (i, elem) { + var $elem = $(elem); + if ($elem.css("display") !== "none") { + availableHt -= $elem.outerHeight(); + } + }); + + // Clip value to 0 (it could be negative if a panel wants more space than we have) + return Math.max(availableHt, 0); + } + + /** Updates panel resize limits to disallow making panels big enough to shrink editor area below 0 */ + function updateResizeLimits() { + var editorAreaHeight = $editorHolder.height(); + + $editorHolder.siblings().each(function (i, elem) { + var $elem = $(elem); + if ($elem.css("display") === "none") { + $elem.data("maxsize", editorAreaHeight); + } else { + $elem.data("maxsize", editorAreaHeight + $elem.outerHeight()); + } + }); + } + + + /** + * Calculates a new size for editor-holder and resizes it accordingly, then and dispatches the "editorAreaResize" + * event. (The editors within are resized by EditorManager, in response to that event). + * + * @param {string=} refreshHint One of "skip", "force", or undefined. See EditorManager docs. + */ + function triggerEditorResize(refreshHint) { + // Find how much space is left for the editor + var editorAreaHeight = calcEditorHeight(); + + $editorHolder.height(editorAreaHeight); // affects size of "not-editor" placeholder as well + + // Resize editor to fill the space + $(exports).trigger("editorAreaResize", [editorAreaHeight, refreshHint]); + } + + + /** Trigger editor area resize whenever the window is resized */ + function handleWindowResize() { + // Immediately adjust editor's height, but skip the refresh since CodeMirror will call refresh() + // itself when it sees the window resize event + triggerEditorResize("skip"); + + if (!windowResizing) { + windowResizing = true; + + // We don't need any fancy debouncing here - we just need to react before the user can start + // resizing any panels at the new window size. So just listen for first mousemove once the + // window resize releases mouse capture. + $(window.document).one("mousemove", function () { + windowResizing = false; + updateResizeLimits(); + }); + } + } + + /** Trigger editor area resize whenever the given panel is shown/hidden/resized */ + function listenToResize($panel) { + // Update editor height when shown/hidden, & continuously as panel is resized + $panel.on("panelCollapsed panelExpanded panelResizeUpdate", function () { + triggerEditorResize(); + }); + // Update max size of sibling panels when shown/hidden, & at *end* of resize gesture + $panel.on("panelCollapsed panelExpanded panelResizeEnd", function () { + updateResizeLimits(); + }); + } + + + /** + * Represents a panel below the editor area (a child of ".content"). + * + * @param {!jQueryObject} $panel The entire panel, including any chrome, already in the DOM. + * @param {number=} minSize Minimum height of panel in px; default is 0 + */ + function Panel($panel, minSize) { + this.$panel = $panel; + + Resizer.makeResizable($panel[0], Resizer.DIRECTION_VERTICAL, Resizer.POSITION_TOP, minSize, false, undefined, true); + listenToResize($panel); + } + + /** @type {jQueryObject} */ + Panel.prototype.$panel = null; + + Panel.prototype.isVisible = function () { + return this.$panel.is(":visible"); + }; + + Panel.prototype.show = function () { + Resizer.show(this.$panel[0]); + }; + Panel.prototype.hide = function () { + Resizer.hide(this.$panel[0]); + }; + + Panel.prototype.setVisible = function (visible) { + if (visible) { + Resizer.show(this.$panel[0]); + } else { + Resizer.hide(this.$panel[0]); + } + }; + + + /** + * Creates a new panel beneath the editor area and above the status bar footer. Panel is initially invisible. + * + * @param {!string} id Unique id for this panel. Use package-style naming, e.g. "myextension.feature.panelname" + * @param {!jQueryObject} $panel DOM content to use as the panel. Need not be in the document yet. + * @param {number=} minSize Minimum height of panel in px; default is 0 + * @return {!Panel} + */ + function createBottomPanel(id, $panel, minSize) { + $panel.insertBefore("#status-bar"); + $panel.hide(); + updateResizeLimits(); // initialize panel's max size + + return new Panel($panel, minSize); + } + + + /** + * Used by EditorManager to notify us of layout changes our normal panel/window listeners wouldn't detect. + * For internal use only: most code should call EditorManager.resizeEditor(). + */ + function _notifyLayoutChange(refreshHint) { + triggerEditorResize(refreshHint); + updateResizeLimits(); + } + + + // Attach to key parts of the overall UI, once created + AppInit.htmlReady(function () { + $windowContent = $(".content"); + $editorHolder = $("#editor-holder"); + + // Sidebar is a special case: a side panel rather than a bottom panel. It could still affect the + // editor-holder's height if changing .content's width causes the inBrowser titlebar to wrap/unwrap. + if (brackets.inBrowser) { + listenToResize($("#sidebar")); + } + }); + + // Unit test only: allow passing in mock DOM notes, e.g. for use with SpecRunnerUtils.createMockEditor() + function _setMockDOM($mockWindowContent, $mockEditorHolder) { + $windowContent = $mockWindowContent; + $editorHolder = $mockEditorHolder; + } + + // If someone adds a panel in the .content stack the old way, make sure we still listen for resize/show/hide + // (Resizer emits a deprecation warning for us - no need to log anything here) + $(Resizer).on("deprecatedPanelAdded", function (event, $panel) { + listenToResize($panel); + }); + + // Add this as a capture handler so we're guaranteed to run it before the editor does its own + // refresh on resize. + window.addEventListener("resize", handleWindowResize, true); + + + // Define public API + exports.createBottomPanel = createBottomPanel; + exports._notifyLayoutChange = _notifyLayoutChange; + exports._setMockDOM = _setMockDOM; +}); diff --git a/src/widgets/StatusBar.js b/src/widgets/StatusBar.js index d195611c24b..deec2e13a60 100644 --- a/src/widgets/StatusBar.js +++ b/src/widgets/StatusBar.js @@ -1,3 +1,27 @@ +/* + * 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, document, Mustache */ diff --git a/test/spec/EditorManager-test.js b/test/spec/EditorManager-test.js index 464127bb5ea..b682694c722 100644 --- a/test/spec/EditorManager-test.js +++ b/test/spec/EditorManager-test.js @@ -29,6 +29,7 @@ define(function (require, exports, module) { 'use strict'; var EditorManager = require("editor/EditorManager"), + PanelManager = require("view/PanelManager"), SpecRunnerUtils = require("spec/SpecRunnerUtils"); describe("EditorManager", function () { @@ -37,20 +38,25 @@ define(function (require, exports, module) { var testEditor, testDoc, $root, $fakeContentDiv; beforeEach(function () { - var mock = SpecRunnerUtils.createMockEditor(""); - testEditor = mock.editor; - testDoc = mock.doc; - $root = $(testEditor.getRootElement()); - // Normally the editor holder would be created inside a "content" div, which is // used in the available height calculation. We create a fake content div just to - // hold the height, and move the editor holder into it. + // hold the height, and we'll place the editor holder in it. $fakeContentDiv = $("
") .css("height", "200px") .appendTo(document.body); + + // createMockEditor() creates the mock-editor-holder, and links EditorManager/PanelManager + // to it (and links PanelManager to our content div) + var mock = SpecRunnerUtils.createMockEditor(""); + + // move newly created mock-editor-holder into the content div we created above $("#mock-editor-holder") .appendTo($fakeContentDiv); + testEditor = mock.editor; + testDoc = mock.doc; + $root = $(testEditor.getRootElement()); + testDoc._masterEditor = testEditor; EditorManager._doShow(testDoc); }); @@ -72,7 +78,7 @@ define(function (require, exports, module) { EditorManager.resizeEditor(); // cache the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_FORCE); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_FORCE); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -83,7 +89,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_FORCE); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_FORCE); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -94,7 +100,7 @@ define(function (require, exports, module) { $root.height(300); // change the height (to be different from content div) spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_FORCE); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_FORCE); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -106,7 +112,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_FORCE); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_FORCE); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -117,7 +123,7 @@ define(function (require, exports, module) { EditorManager.resizeEditor(); // cache the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_SKIP); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_SKIP); expect(testEditor.refreshAll).not.toHaveBeenCalled(); }); @@ -128,7 +134,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_SKIP); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_SKIP); expect(testEditor.refreshAll).not.toHaveBeenCalled(); }); @@ -139,7 +145,7 @@ define(function (require, exports, module) { $root.height(300); // change the height (to be different from content div) spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_SKIP); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_SKIP); expect(testEditor.refreshAll).not.toHaveBeenCalled(); }); @@ -151,7 +157,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(EditorManager.REFRESH_SKIP); + PanelManager._notifyLayoutChange(EditorManager.REFRESH_SKIP); expect(testEditor.refreshAll).not.toHaveBeenCalled(); }); @@ -162,7 +168,7 @@ define(function (require, exports, module) { EditorManager.resizeEditor(); // cache the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(); + PanelManager._notifyLayoutChange(); expect(testEditor.refreshAll).not.toHaveBeenCalled(); }); @@ -173,7 +179,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(); + PanelManager._notifyLayoutChange(); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -184,7 +190,7 @@ define(function (require, exports, module) { $root.height(300); // change the height (to be different from content div) spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(); + PanelManager._notifyLayoutChange(); expect(testEditor.refreshAll).toHaveBeenCalled(); }); @@ -196,7 +202,7 @@ define(function (require, exports, module) { $root.width(300); // change the width spyOn(testEditor, "refreshAll"); - EditorManager.resizeEditor(); + PanelManager._notifyLayoutChange(); expect(testEditor.refreshAll).toHaveBeenCalled(); }); }); diff --git a/test/spec/SpecRunnerUtils.js b/test/spec/SpecRunnerUtils.js index bb7c726d98b..29b26a716d8 100644 --- a/test/spec/SpecRunnerUtils.js +++ b/test/spec/SpecRunnerUtils.js @@ -34,6 +34,7 @@ define(function (require, exports, module) { DocumentManager = require("document/DocumentManager"), Editor = require("editor/Editor").Editor, EditorManager = require("editor/EditorManager"), + PanelManager = require("view/PanelManager"), ExtensionLoader = require("utils/ExtensionLoader"), UrlParams = require("utils/UrlParams").UrlParams, LanguageManager = require("language/LanguageManager"); @@ -204,24 +205,34 @@ define(function (require, exports, module) { } /** - * Returns a Document and Editor suitable for use with an Editor in - * isolation: i.e., a Document that will never be set as the - * currentDocument or added to the working set. - * @return {!{doc:{Document}, editor:{Editor}}} + * Returns an Editor tied to the given Document, but suitable for use in isolation + * (without being placed inside the surrounding Brackets UI). + * @return {!Editor} */ - function createMockEditor(initialContent, languageId, visibleRange) { - // Initialize EditorManager and position the editor-holder offscreen + function createMockEditorForDocument(doc, visibleRange) { + // Initialize EditorManager/PanelManager and position the editor-holder offscreen + // (".content" may not exist, but that's ok for headless tests where editor height doesn't matter) var $editorHolder = createMockElement().attr("id", "mock-editor-holder"); + PanelManager._setMockDOM($(".content"), $editorHolder); EditorManager.setEditorHolder($editorHolder); - // create dummy Document for the Editor - var doc = createMockDocument(initialContent, languageId); - // create Editor instance var editor = new Editor(doc, true, $editorHolder.get(0), visibleRange); EditorManager._notifyActiveEditorChanged(editor); - return { doc: doc, editor: editor }; + return editor; + } + + /** + * Returns a Document and Editor suitable for use in isolation: i.e., the Document + * will never be set as the currentDocument or added to the working set and the + * Editor does not live inside a full-blown Brackets UI layout. + * @return {!{doc:!Document, editor:!Editor}} + */ + function createMockEditor(initialContent, languageId, visibleRange) { + // create dummy Document, then Editor tied to it + var doc = createMockDocument(initialContent, languageId); + return { doc: doc, editor: createMockEditorForDocument(doc, visibleRange) }; } /** @@ -941,6 +952,7 @@ define(function (require, exports, module) { exports.createMockDocument = createMockDocument; exports.createMockActiveDocument = createMockActiveDocument; exports.createMockElement = createMockElement; + exports.createMockEditorForDocument = createMockEditorForDocument; exports.createMockEditor = createMockEditor; exports.createTestWindowAndRun = createTestWindowAndRun; exports.closeTestWindow = closeTestWindow;