diff --git a/src/brackets.js b/src/brackets.js index 102acafbe1f..c5e53ce5d3a 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -91,6 +91,7 @@ define(function (require, exports, module) { UrlParams = require("utils/UrlParams").UrlParams, NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem, PreferencesManager = require("preferences/PreferencesManager"), + Resizer = require("utils/Resizer"), StatusBar = require("widgets/Statusbar"); // Local variables diff --git a/src/htmlContent/main-view.html b/src/htmlContent/main-view.html index a1fd02a4175..8380f4f0c64 100644 --- a/src/htmlContent/main-view.html +++ b/src/htmlContent/main-view.html @@ -90,19 +90,20 @@ -
+
{{JSLINT_ERRORS}}
-
+
-
+ +
{{SEARCH_RESULTS}}
×
-
+
diff --git a/src/language/JSLintUtils.js b/src/language/JSLintUtils.js index 5e2c732628d..bd040d036ac 100644 --- a/src/language/JSLintUtils.js +++ b/src/language/JSLintUtils.js @@ -23,7 +23,7 @@ /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ -/*global define, $, brackets, JSLINT, PathUtils */ +/*global define, $, JSLINT, PathUtils */ /** * Allows JSLint to run on the current document and report results in a UI panel. @@ -47,7 +47,15 @@ define(function (require, exports, module) { Strings = require("strings"), StringUtils = require("utils/StringUtils"), AppInit = require("utils/AppInit"), + Resizer = require("utils/Resizer"), StatusBar = require("widgets/StatusBar"); + + var PREFERENCES_CLIENT_ID = module.id, + defaultPrefs = { height: 200, enabled: true }; + + /** @type {Number} Height of the JSLint panel header in pixels. Hardcoded to avoid race + condition when measuring it on htmlReady*/ + var HEADER_HEIGHT = 27; /** * @private @@ -212,20 +220,33 @@ define(function (require, exports, module) { setEnabled(!getEnabled()); } - // Register command handlers CommandManager.register(Strings.CMD_JSLINT, Commands.TOGGLE_JSLINT, _handleToggleJSLint); // Init PreferenceStorage - _prefs = PreferencesManager.getPreferenceStorage(module.id, { enabled: !!brackets.config.enable_jslint }); + _prefs = PreferencesManager.getPreferenceStorage(PREFERENCES_CLIENT_ID, defaultPrefs); _setEnabled(_prefs.getValue("enabled")); - // Init StatusBar indicator + // Initialize items dependent on HTML DOM AppInit.htmlReady(function () { + var height = Math.max(_prefs.getValue("height"), 100), + $jslintResults = $("#jslint-results"), + $jslintContent = $("#jslint-results .table-container"); + + $jslintResults.height(height); + $jslintContent.height(height - HEADER_HEIGHT); + + if (_enabled) { + EditorManager.resizeEditor(); + } + + $jslintResults.on("panelResizeEnd", function (event, height) { + _prefs.setValue("height", height); + }); + StatusBar.addIndicator(module.id, $("#gold-star"), false); }); - - + // Define public API exports.run = run; exports.getEnabled = getEnabled; diff --git a/src/search/FindInFiles.js b/src/search/FindInFiles.js index 12737095029..4c90306dce1 100644 --- a/src/search/FindInFiles.js +++ b/src/search/FindInFiles.js @@ -50,13 +50,23 @@ define(function (require, exports, module) { DocumentManager = require("document/DocumentManager"), EditorManager = require("editor/EditorManager"), FileIndexManager = require("project/FileIndexManager"), + PreferencesManager = require("preferences/PreferencesManager"), KeyEvent = require("utils/KeyEvent"), + AppInit = require("utils/AppInit"), + Resizer = require("utils/Resizer"), StatusBar = require("widgets/StatusBar"); var searchResults = []; var FIND_IN_FILES_MAX = 100; - + + var PREFERENCES_CLIENT_ID = module.id, + defaultPrefs = { height: 200 }; + + /** @type {Number} Height of the FIF panel header in pixels. Hardcoded to avoid race + condition when measuring it on htmlReady*/ + var HEADER_HEIGHT = 27; + // This dialog class was mostly copied from QuickOpen. We should have a common dialog // class that everyone can use. @@ -340,6 +350,21 @@ define(function (require, exports, module) { } }); } + + // Initialize items dependent on HTML DOM + AppInit.htmlReady(function () { + var $searchResults = $("#search-results"), + $searchContent = $("#search-results .table-container"), + prefs = PreferencesManager.getPreferenceStorage(module.id, defaultPrefs), + height = prefs.getValue("height"); + + $searchResults.height(height); + $searchContent.height(height - HEADER_HEIGHT); + + $searchResults.on("panelResizeEnd", function (event, height) { + prefs.setValue("height", height); + }); + }); function _fileNameChangeHandler(event, oldName, newName) { if ($("#search-results").is(":visible")) { @@ -353,4 +378,4 @@ define(function (require, exports, module) { $(DocumentManager).on("fileNameChange", _fileNameChangeHandler); CommandManager.register(Strings.CMD_FIND_IN_FILES, Commands.EDIT_FIND_IN_FILES, doFindInFiles); -}); \ No newline at end of file +}); diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 9caf9a17656..37aa9b222dd 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -62,11 +62,13 @@ html, body { body { .vbox; - /* This appears to be necessary in Firefox when body is set to display: box. */ - width: 100%; &.resizing a, &.resizing #projects a, &.resizing .main-view, &.resizing .CodeMirror-lines { cursor: col-resize; } + + &.vert-resizing a, &.vert-resizing #projects a, &.vert-resizing .main-view, &.vert-resizing .CodeMirror-lines { + cursor: row-resize; + } } @@ -180,14 +182,32 @@ a, img { background: @background-color-2 url('images/no_content_bg.svg') no-repeat center 45%; } } - + +.vert-resizer { + position: absolute; + height: 6px; + width: 100%; + z-index: @z-index-brackets-panel-resizer; + opacity: 0; + cursor: row-resize; +} + +.horz-resizer { + position: absolute; + height: 100%; + width: 6px; + z-index: @z-index-brackets-panel-resizer; + opacity: 0; + cursor: col-resize; +} + .bottom-panel { display: none; height: 200px; border-top-style: solid; border-width: 1px; border-color: lighten(@bc-grey, @bc-color-step-size*4); - + .toolbar { height: auto; padding-top: @base-padding / 2; diff --git a/src/styles/brackets_variables.less b/src/styles/brackets_variables.less index 170bf59b3f4..79a1d61fd78 100644 --- a/src/styles/brackets_variables.less +++ b/src/styles/brackets_variables.less @@ -50,5 +50,6 @@ @z-index-brackets-sidebar-resizer: @z-index-brackets-ui + 2; @z-index-brackets-resizer-div: @z-index-brackets-sidebar-resizer + 1; +@z-index-brackets-panel-resizer: @z-index-brackets-ui + 2; @z-index-brackets-context-menu-base: 1000; diff --git a/src/utils/Resizer.js b/src/utils/Resizer.js new file mode 100644 index 00000000000..c380034d48a --- /dev/null +++ b/src/utils/Resizer.js @@ -0,0 +1,197 @@ +/* + * 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, $, window */ + +/** + * Resizer is a Module utility to inject resizing capabilities to any element + * inside Brackets. + * + * On initialization, Resizer discovers all nodes tagged as "vert-resizable" + * and "horz-resizable" to add the resizer handler. Additionally, "top-resizer", + * "bottom-resizer", "left-resizer" and "right-resizer" classes control the + * position of the resizer on the element. + * + * An element can be made resizable at any time using the `makeResizable` API + * + * The resizable elements trigger a panelResizeStart, panelResizeUpdate and panelResizeEnd + * event that can be used to create performance optimizations (such as hiding/showing elements + * while resizing), custom or internal resizes and save the final resized value into local + * storage for example. + * + * TODO Trigger panelResizeStart and panelResizeUpdate as required. They aren't needed + * currently. + */ +define(function (require, exports, module) { + "use strict"; + + var DIRECTION_VERTICAL = "vert"; + var DIRECTION_HORIZONTAL = "horz"; + + var POSITION_TOP = "top"; + var POSITION_BOTTOM = "bottom"; + var POSITION_LEFT = "left"; + var POSITION_RIGHT = "right"; + + // Minimum size (height or width) for autodiscovered resizable panels + var DEFAULT_MIN_SIZE = 100; + + // Load dependent modules + var AppInit = require("utils/AppInit"), + EditorManager = require("editor/EditorManager"); + + var $mainView; + + /** + * Adds resizing capabilities to a given html element. + * + * Resizing can be configured in two directions: + * - Vertical ("vert"): Resizes the height of the element + * - Horizontal ("horz"): Resizes the width of the element + * + * Resizer handlers can be positioned on the element at: + * - Top ("top") or bottom ("bottom") for vertical resizing + * - Left ("left") or right ("right") for horizontal resizing + * + * A resizable element triggers the following events while resizing: + * - panelResizeEnds: When the resize ends + * + * @param {DOMNode} element Html element which should be made resizable. + * @param {string} direction The direction of the resize action. Must be "horz" or "vert". + * @param {string} position The position of the resizer on the element. Can be "top" or "bottom" + * for vertical resizing and "left" or "right" for horizontal resizing. + * @param {int} minSize Minimum size (width or height) of the element. + */ + function makeResizable(element, direction, position, minSize) { + + var $resizer = $('
'), + $element = $(element), + $resizableElement = $($element.find(".resizable-content:first")[0]), + $body = $(window.document.body), + animationRequest = null, + directionProperty = direction === DIRECTION_HORIZONTAL ? "clientX" : "clientY", + elementSizeFunction = direction === DIRECTION_HORIZONTAL ? $element.width : $element.height, + contentSizeFunction = null; + + minSize = minSize || 0; + + $element.prepend($resizer); + + $resizer.on("mousedown", function (e) { + var startPosition = e[directionProperty], + startSize = elementSizeFunction.apply($element), + newSize = startSize, + baseSize = 0, + doResize = true, + isMouseDown = true; + + if ($resizableElement !== undefined) { + $element.children().not(".horz-resizer, .vert-resizer, .resizable-content").each(function (index, child) { + if (direction === DIRECTION_HORIZONTAL) { + baseSize += $(child).outerWidth(); + } else { + baseSize += $(child).outerHeight(); + } + }); + + contentSizeFunction = direction === DIRECTION_HORIZONTAL ? $resizableElement.width : $resizableElement.height; + } + + $body.toggleClass(direction + "-resizing"); + + animationRequest = window.webkitRequestAnimationFrame(function doRedraw() { + // only run this if the mouse is down so we don't constantly loop even + // after we're done resizing. + if (!isMouseDown) { + return; + } + + if (doResize) { + // resize the main element to the new size + elementSizeFunction.apply($element, [newSize]); + + // if there is a content element, its size is the new size + // minus the size of the non-resizable elements + if ($resizableElement !== undefined) { + contentSizeFunction.apply($resizableElement, [newSize - baseSize]); + } + + EditorManager.resizeEditor(); + } + + animationRequest = window.webkitRequestAnimationFrame(doRedraw); + }); + + $mainView.on("mousemove", function (e) { + // calculate newSize adding to startSize the difference + // between starting and current position, capped at minSize + newSize = Math.max(startSize + (startPosition - e[directionProperty]), minSize); + e.preventDefault(); + }); + + function endResize(e) { + if (isMouseDown) { + isMouseDown = false; + $mainView.off("mousemove"); + $body.toggleClass(direction + "-resizing"); + $element.trigger("panelResizeEnd", [elementSizeFunction.apply($element)]); + } + } + + $mainView.one("mouseup", endResize); + $mainView.mouseleave(endResize); + + e.preventDefault(); + }); + } + + // Scan DOM for horz-resizable and vert-resizable classes and make them resizable + AppInit.htmlReady(function () { + $mainView = $(".main-view"); + + $(".vert-resizable").each(function (index, element) { + + if ($(element).hasClass("top-resizer")) { + makeResizable(element, DIRECTION_VERTICAL, POSITION_TOP, DEFAULT_MIN_SIZE); + } + + //if ($(element).hasClass("bottom-resizer")) { + // makeResizable(element, DIRECTION_VERTICAL, POSITION_BOTTOM, DEFAULT_MIN_SIZE); + //} + }); + + $(".horz-resizable").each(function (index, element) { + + //if ($(element).hasClass("left-resizer")) { + // makeResizable(element, DIRECTION_HORIZONTAL, POSITION_LEFT, DEFAULT_MIN_SIZE); + //} + + //if ($(element).hasClass("right-resizer")) { + // makeResizable(element, DIRECTION_HORIZONTAL, POSITION_RIGHT, DEFAULT_MIN_SIZE); + //} + }); + }); + + exports.makeResizable = makeResizable; +}); \ No newline at end of file