From 9b0022362a83742b0fa56c3934b4f75d5a23fc24 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 19 Apr 2012 11:19:26 -0700 Subject: [PATCH 1/7] New sidebarList util to add selection highlight and triangle indicator to project panel --- src/editor/CSSInlineEditor.js | 2 + src/project/ProjectManager.js | 14 ++++ src/project/WorkingSetView.js | 7 +- src/styles/brackets.less | 42 ++++++++---- src/styles/brackets_mixins.less | 12 ++-- src/styles/brackets_patterns_override.less | 1 - src/styles/brackets_variables.less | 5 +- src/styles/jsTreeTheme.less | 1 - src/utils/ViewUtils.js | 77 +++++++++++++++++++++- 9 files changed, 138 insertions(+), 23 deletions(-) diff --git a/src/editor/CSSInlineEditor.js b/src/editor/CSSInlineEditor.js index 2f8328dbb36..82903f485d8 100644 --- a/src/editor/CSSInlineEditor.js +++ b/src/editor/CSSInlineEditor.js @@ -96,6 +96,8 @@ define(function (require, exports, module) { this.$relatedContainer = $(document.createElement("div")).addClass("relatedContainer"); this._relatedContainerInserted = false; this._relatedContainerInsertedHandler = this._relatedContainerInsertedHandler.bind(this); + + // FIXME (jasonsj): deprecated event http://www.w3.org/TR/DOM-Level-3-Events/ this.$relatedContainer.on("DOMNodeInserted", this._relatedContainerInsertedHandler); // List "selection" highlight diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index 016ab9a55e1..c7948ec4693 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -44,6 +44,13 @@ define(function (require, exports, module) { */ var _projectTree = null; + /** + * @private + * Reference to the tree control UL element + * @type {DOMElement} + */ + var $projectTreeList; + /** * @private * @see getProjectRoot() @@ -79,6 +86,9 @@ define(function (require, exports, module) { } else if (_projectTree !== null) { _projectTree.jstree("deselect_all"); } + + // redraw selection + $projectTreeList.trigger("selectionChanged"); }; $(FileViewController).on("documentSelectionFocusChange", _documentSelectionFocusChange); @@ -259,6 +269,10 @@ define(function (require, exports, module) { FileViewController.addToWorkingSetAndSelect(entry.fullPath); } }); + + // fire selection changed events for sidebarSelection + $projectTreeList = $projectTreeContainer.find("ul"); + ViewUtils.sidebarList($projectTreeContainer, "jstree-clicked"); }); return result; diff --git a/src/project/WorkingSetView.js b/src/project/WorkingSetView.js index a42fab45ed8..64f93806c1d 100644 --- a/src/project/WorkingSetView.js +++ b/src/project/WorkingSetView.js @@ -26,7 +26,8 @@ define(function (require, exports, module) { * Use listItem.data(_FILE_KEY) to get the document reference */ var _FILE_KEY = "file", - $openFilesContainer = $("#open-files-container"); + $openFilesContainer = $("#open-files-container"), + $openFilesList = $openFilesContainer.find("ul"); function _hideShowOpenFileHeader() { if (DocumentManager.getWorkingSet().length === 0) { @@ -248,10 +249,14 @@ define(function (require, exports, module) { $(FileViewController).on("documentSelectionFocusChange", function (event, eventTarget) { _handleDocumentSelectionChange(); + + // redraw selection + $openFilesList.trigger("selectionChanged"); }); _hideShowOpenFileHeader(); // Show scroller shadows when open-files-container scrolls ViewUtils.installScrollShadow($openFilesContainer[0]); + ViewUtils.sidebarList($openFilesContainer); }); diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 682edfbedb4..a974d33938d 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -192,15 +192,12 @@ a, img { border-bottom: 1px solid #9a9a9a; box-shadow: 0px 1px #eeeeee; } - - } #open-files-container { .box-flex(0); margin: 0 0 22px 0; padding: 0px; - overflow: auto; max-height: 200px; // TODO (Issue #276): it would be nicer to have this be 50%, but that doesn't seem to work @@ -225,6 +222,7 @@ a, img { color: #BBB; font-size: 14px; margin-left: 18px; + padding-right: @triangle-size * 2; cursor: default; } .extension a { @@ -237,8 +235,6 @@ a, img { background-position: 0 1px; } &.selected { - height: 25px; - background: url("styles/images/active_back.png") no-repeat top right; a { color: #fff; font-weight: normal; @@ -251,6 +247,27 @@ a, img { } } +.sidebarSelection { + background: url("styles/images/active_back.png") no-repeat top right; + height: 25px; + position: absolute; +} + +.sidebarSelection div { + position: absolute; + margin-top: -@triangle-size; + top: 50%; + right: 0px; + + border-top: @triangle-size solid transparent; + border-bottom: @triangle-size solid transparent; + border-right: @triangle-size solid @background-color-3; + + width: 0; + height: 0; + .scaleX(0.9, right, top); +} + //Initially start with the open files hidden, they will get show as files are added #open-files-container { display:none; @@ -258,12 +275,14 @@ a, img { #project-files-container { .box-flex(1); - padding: 0px; - padding-left: 8px; - margin: 0; - overflow: auto; - ul { margin-top: 0; } + .jstree-brackets li > a { + padding-right: @triangle-size * 2; + } + + ul { + padding-left: 8px; + } } .scrollerShadow { @@ -418,7 +437,6 @@ a, img { * (b) Use transform scaleX and origin to adjust width. */ .selection:before { - @triangle-size: 10px; content: " "; position: absolute; width: 0; @@ -428,7 +446,7 @@ a, img { border-left: @triangle-size solid @inline-background-color-1; margin-top: -@triangle-size; top: 50%; - .scaleX(0.9); + .scaleX(0.9, left, top); } .related { diff --git a/src/styles/brackets_mixins.less b/src/styles/brackets_mixins.less index 91da7602a6f..4c4f91ffa72 100644 --- a/src/styles/brackets_mixins.less +++ b/src/styles/brackets_mixins.less @@ -66,16 +66,16 @@ /* Scale x-axis using top-left as the origin */ -.scaleX (@value: 1.0) { +.scaleX (@value: 1.0, @horizontal: 50%, @vertical: 50%) { -ms-transform: scaleX(@value); -moz-transform: scaleX(@value); -webkit-transform: scaleX(@value); -o-transform: scaleX(@value); transform: scaleX(@value); - -ms-transform-origin: 0 0; - -moz-transform-origin: 0 0; - -webkit-transform-origin: 0 0; - -o-transform-origin: 0 0; - transform-origin: 0 0; + -ms-transform-origin: @horizontal @vertical; + -moz-transform-origin: @horizontal @vertical; + -webkit-transform-origin: @horizontal @vertical; + -o-transform-origin: @horizontal @vertical; + transform-origin: @horizontal @vertical; } \ No newline at end of file diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 11af215f425..03231a329ac 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -26,7 +26,6 @@ padding: 0; margin: 0; - border-right: 1px solid #4e5153; .project-panel-background; color: #bbb; diff --git a/src/styles/brackets_variables.less b/src/styles/brackets_variables.less index c3d04b6ac6e..3c94e74fdf5 100644 --- a/src/styles/brackets_variables.less +++ b/src/styles/brackets_variables.less @@ -5,4 +5,7 @@ /* Common font sizes */ @title-font-size: 18px; // headings such as the editor titlebar -@label-font-size: 14px; // labels on buttons, menubar, etc. \ No newline at end of file +@label-font-size: 14px; // labels on buttons, menubar, etc. + +/* CSS triangle */ +@triangle-size: 10px; \ No newline at end of file diff --git a/src/styles/jsTreeTheme.less b/src/styles/jsTreeTheme.less index bbad99357b5..cdc9bf83e5d 100644 --- a/src/styles/jsTreeTheme.less +++ b/src/styles/jsTreeTheme.less @@ -11,7 +11,6 @@ .jstree-brackets .jstree-clicked { color: #fff; - background: url("styles/images/active_back.png") no-repeat top right; } diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index 5fd546b0fa1..1ec49b247f3 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -58,9 +58,84 @@ define(function (require, exports, module) { // update immediately _updateScrollerShadow(displayElement, scrollElement); } + + /** + * Within a scrolling DOMElement, creates and positions a styled selection + * div to align a single selected list item. + * + * @param {!DOMElement} scrollElement A DOMElement containing a list + * @param {!string} selectedClassName A CSS class name on at most one list item in the contained list + */ + function sidebarList($scrollElement, selectedClassName) { + var $listElement = $scrollElement.find("ul"), + $listItem, + $selectionMarker, + $selectionTriangle, + top, + right; + + // build selectionMarker with background and triangle visuals + $selectionMarker = $(document.createElement("div")).prependTo($scrollElement).addClass("sidebarSelection"); + $selectionTriangle = $(document.createElement("div")); + $selectionMarker.append($selectionTriangle); + + // enable scrolling + $scrollElement.css("overflow", "auto"); + + // use relative postioning for clipping the selectionMarker within the scrollElement + $scrollElement.css("position", "relative"); + + selectedClassName = "." + (selectedClassName || "selected"); + + var updateSelectionTriangle = function () { + right = $scrollElement[0].scrollWidth - $scrollElement.width(); + $selectionTriangle.css("right", Math.abs($scrollElement[0].scrollLeft - right)); + }; + + var updateSelectionMarker = function () { + // find the selected list item + $listItem = $listElement.find(selectedClassName).closest("li"); + + if ($listItem.length === 1) { + // list item position is relative to it's immediate parent UL + top = $listItem.position().top; + + // determine top position relative to scroller by sum of nested list items + $.each($listItem.parentsUntil($scrollElement, "li"), function (index, ancestor) { + top += $(ancestor).position().top; + }); + + // offset by current scroll position + top += $scrollElement[0].scrollTop; + + // move the selectionMarker position to align with the list item + $selectionMarker.css("top", top); + + // force selection width to match scroller + $selectionMarker.width($scrollElement[0].scrollWidth); + + $selectionMarker.show(); + } else { + // hide the selection marker when no selection is found + $selectionMarker.hide(); + } + + updateSelectionTriangle(); + }; + + $listElement.bind("selectionChanged", updateSelectionMarker); + + // position triangle + $scrollElement.bind("scroll", updateSelectionTriangle); + + // update immediately + updateSelectionMarker(); + } // Define public API + exports.SCROLL_SHADOW_HEIGHT = SCROLL_SHADOW_HEIGHT; + exports.updateChildrenToParentScrollwidth = updateChildrenToParentScrollwidth; exports.installScrollShadow = installScrollShadow; - exports.SCROLL_SHADOW_HEIGHT = SCROLL_SHADOW_HEIGHT; + exports.sidebarList = sidebarList; }); From 65c98648290367b9e7a447c8e4a12edc1ea232b7 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 19 Apr 2012 14:34:33 -0700 Subject: [PATCH 2/7] Try positioning selection triangle to the outer file-section div --- src/styles/brackets.less | 4 ++-- src/utils/ViewUtils.js | 39 +++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/styles/brackets.less b/src/styles/brackets.less index a974d33938d..34d72f8e799 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -183,6 +183,7 @@ a, img { .vbox; .box-flex(1); margin: 5px 0px 0px 0px; + position: relative; .project-file-header-area { padding: 6px 6px 2px 10px; @@ -253,10 +254,9 @@ a, img { position: absolute; } -.sidebarSelection div { +.sidebarSelectionTriangle { position: absolute; margin-top: -@triangle-size; - top: 50%; right: 0px; border-top: @triangle-size solid transparent; diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index 1ec49b247f3..78f898a2ee7 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -71,13 +71,14 @@ define(function (require, exports, module) { $listItem, $selectionMarker, $selectionTriangle, - top, - right; + $fileSection = $("#file-section"), + selectionMarkerTop, + selectionTriangleTop; // build selectionMarker with background and triangle visuals $selectionMarker = $(document.createElement("div")).prependTo($scrollElement).addClass("sidebarSelection"); - $selectionTriangle = $(document.createElement("div")); - $selectionMarker.append($selectionTriangle); + $selectionTriangle = $(document.createElement("div")).addClass("sidebarSelectionTriangle"); + $fileSection.append($selectionTriangle); // enable scrolling $scrollElement.css("overflow", "auto"); @@ -87,46 +88,48 @@ define(function (require, exports, module) { selectedClassName = "." + (selectedClassName || "selected"); - var updateSelectionTriangle = function () { - right = $scrollElement[0].scrollWidth - $scrollElement.width(); - $selectionTriangle.css("right", Math.abs($scrollElement[0].scrollLeft - right)); - }; - var updateSelectionMarker = function () { // find the selected list item $listItem = $listElement.find(selectedClassName).closest("li"); if ($listItem.length === 1) { // list item position is relative to it's immediate parent UL - top = $listItem.position().top; + selectionMarkerTop = $listItem.position().top; // determine top position relative to scroller by sum of nested list items $.each($listItem.parentsUntil($scrollElement, "li"), function (index, ancestor) { - top += $(ancestor).position().top; + selectionMarkerTop += $(ancestor).position().top; }); + // triangle top position is relative to file-section div + selectionTriangleTop = selectionMarkerTop; + // offset by current scroll position - top += $scrollElement[0].scrollTop; + selectionMarkerTop += $scrollElement[0].scrollTop; // move the selectionMarker position to align with the list item - $selectionMarker.css("top", top); + $selectionMarker.css("top", selectionMarkerTop); + // adjust triangle position by scrollElement top offset + selectionTriangleTop += $scrollElement.position().top + (Math.sqrt(200) * 0.9); // FIXME height / 2 + + $selectionTriangle.css("top", selectionTriangleTop); + $selectionTriangle.css("right", -$selectionTriangle.width()); + // force selection width to match scroller $selectionMarker.width($scrollElement[0].scrollWidth); + $selectionTriangle.show(); $selectionMarker.show(); } else { // hide the selection marker when no selection is found + $selectionTriangle.hide(); $selectionMarker.hide(); } - - updateSelectionTriangle(); }; $listElement.bind("selectionChanged", updateSelectionMarker); - - // position triangle - $scrollElement.bind("scroll", updateSelectionTriangle); + $scrollElement.bind("scroll", updateSelectionMarker); // update immediately updateSelectionMarker(); From e5819aca73e7c0ae8f56fc81c5488a1d8f60799e Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 19 Apr 2012 14:56:21 -0700 Subject: [PATCH 3/7] Try clipping --- src/utils/ViewUtils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index 78f898a2ee7..f3bc639285b 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -115,6 +115,7 @@ define(function (require, exports, module) { $selectionTriangle.css("top", selectionTriangleTop); $selectionTriangle.css("right", -$selectionTriangle.width()); + //$selectionTriangle.css("clip", "rect(" + + " 0px 0px 0px"); // force selection width to match scroller $selectionMarker.width($scrollElement[0].scrollWidth); From 8addb5a60a11181748f5b26b5971c7a6494a9f37 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 20 Apr 2012 10:07:26 -0700 Subject: [PATCH 4/7] Got fixed position and clipping working! --- src/styles/brackets.less | 6 +-- src/utils/ViewUtils.js | 79 +++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 34d72f8e799..370078fabc1 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -255,16 +255,14 @@ a, img { } .sidebarSelectionTriangle { - position: absolute; + position: fixed; + margin-top: -@triangle-size; - right: 0px; border-top: @triangle-size solid transparent; border-bottom: @triangle-size solid transparent; border-right: @triangle-size solid @background-color-3; - width: 0; - height: 0; .scaleX(0.9, right, top); } diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index f3bc639285b..a46be926895 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -66,28 +66,63 @@ define(function (require, exports, module) { * @param {!DOMElement} scrollElement A DOMElement containing a list * @param {!string} selectedClassName A CSS class name on at most one list item in the contained list */ - function sidebarList($scrollElement, selectedClassName) { - var $listElement = $scrollElement.find("ul"), + function sidebarList($scrollerElement, selectedClassName) { + var $listElement = $scrollerElement.find("ul"), $listItem, $selectionMarker, + triangleOffsetYBy, + triangleClipOffsetYBy, $selectionTriangle, $fileSection = $("#file-section"), selectionMarkerTop, - selectionTriangleTop; + triangleTop, + triangleHeight, + triangleBottom, + scrollerOffset, + scrollerTop, + scrollerBottom, + scrollerLeft, + rightOffset; - // build selectionMarker with background and triangle visuals - $selectionMarker = $(document.createElement("div")).prependTo($scrollElement).addClass("sidebarSelection"); - $selectionTriangle = $(document.createElement("div")).addClass("sidebarSelectionTriangle"); - $fileSection.append($selectionTriangle); + // build selectionMarker and position absolute within the scroller + $selectionMarker = $(document.createElement("div")).addClass("sidebarSelection"); + $scrollerElement.prepend($selectionMarker); // enable scrolling - $scrollElement.css("overflow", "auto"); + $scrollerElement.css("overflow", "auto"); // use relative postioning for clipping the selectionMarker within the scrollElement - $scrollElement.css("position", "relative"); + $scrollerElement.css("position", "relative"); + + // build selectionTriangle and position fixed to the windwo + $selectionTriangle = $(document.createElement("div")).addClass("sidebarSelectionTriangle"); + $fileSection.append($selectionTriangle); selectedClassName = "." + (selectedClassName || "selected"); + var updateSelectionTriangle = function () { + scrollerOffset = $scrollerElement.offset(); + scrollerTop = scrollerOffset.top; + scrollerBottom = scrollerTop + $scrollerElement[0].clientHeight; + scrollerLeft = scrollerOffset.left; + + triangleTop = $selectionMarker.offset().top; + triangleHeight = $selectionTriangle.outerHeight(); + triangleOffsetYBy = $selectionMarker.height() / 2; + triangleClipOffsetYBy = Math.floor(($selectionMarker.height() - triangleHeight) / 2); + triangleBottom = triangleTop + triangleHeight + triangleClipOffsetYBy; + + $selectionTriangle.css("top", triangleTop + triangleOffsetYBy); + $selectionTriangle.css("left", $fileSection.width() - $selectionTriangle.outerWidth()); + + if (triangleTop < scrollerTop || triangleBottom > scrollerBottom) { + $selectionTriangle.css("clip", "rect(" + Math.max(scrollerTop - triangleTop - triangleClipOffsetYBy, 0) + "px, auto, " + + (triangleHeight - Math.max(triangleBottom - scrollerBottom, 0)) + "px, auto)"); + } else { + $selectionTriangle.css("clip", ""); + } + }; + var updateSelectionMarker = function () { // find the selected list item $listItem = $listElement.find(selectedClassName).closest("li"); @@ -97,31 +132,23 @@ define(function (require, exports, module) { selectionMarkerTop = $listItem.position().top; // determine top position relative to scroller by sum of nested list items - $.each($listItem.parentsUntil($scrollElement, "li"), function (index, ancestor) { + $.each($listItem.parentsUntil($scrollerElement, "li"), function (index, ancestor) { selectionMarkerTop += $(ancestor).position().top; }); - // triangle top position is relative to file-section div - selectionTriangleTop = selectionMarkerTop; - // offset by current scroll position - selectionMarkerTop += $scrollElement[0].scrollTop; + selectionMarkerTop += $scrollerElement[0].scrollTop; + + // force selection width to match scroller + $selectionMarker.width($scrollerElement[0].scrollWidth); // move the selectionMarker position to align with the list item $selectionMarker.css("top", selectionMarkerTop); + $selectionMarker.show(); - // adjust triangle position by scrollElement top offset - selectionTriangleTop += $scrollElement.position().top + (Math.sqrt(200) * 0.9); // FIXME height / 2 - - $selectionTriangle.css("top", selectionTriangleTop); - $selectionTriangle.css("right", -$selectionTriangle.width()); - //$selectionTriangle.css("clip", "rect(" + + " 0px 0px 0px"); - - // force selection width to match scroller - $selectionMarker.width($scrollElement[0].scrollWidth); + updateSelectionTriangle(); $selectionTriangle.show(); - $selectionMarker.show(); } else { // hide the selection marker when no selection is found $selectionTriangle.hide(); @@ -129,8 +156,8 @@ define(function (require, exports, module) { } }; - $listElement.bind("selectionChanged", updateSelectionMarker); - $scrollElement.bind("scroll", updateSelectionMarker); + $listElement.on("selectionChanged", updateSelectionMarker); + $scrollerElement.on("scroll", updateSelectionTriangle); // update immediately updateSelectionMarker(); From 186b5170461e65f00773a46568fb14d13d092d95 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 20 Apr 2012 13:25:29 -0700 Subject: [PATCH 5/7] Code review comments. Also added scrolling to the initial selection. --- src/utils/ViewUtils.js | 78 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index a46be926895..1b5b90ce873 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -61,28 +61,23 @@ define(function (require, exports, module) { /** * Within a scrolling DOMElement, creates and positions a styled selection - * div to align a single selected list item. + * div to align a single selected list item from a ul list element. + * + * Assumtions: + * - scrollElement is a child of the #file-section div + * - ul list element fires a "selectionChanged" event after the + * selectedClassName is assigned to a new list item * - * @param {!DOMElement} scrollElement A DOMElement containing a list + * @param {!DOMElement} scrollElement A DOMElement containing a ul list element * @param {!string} selectedClassName A CSS class name on at most one list item in the contained list */ function sidebarList($scrollerElement, selectedClassName) { var $listElement = $scrollerElement.find("ul"), $listItem, $selectionMarker, - triangleOffsetYBy, - triangleClipOffsetYBy, $selectionTriangle, $fileSection = $("#file-section"), - selectionMarkerTop, - triangleTop, - triangleHeight, - triangleBottom, - scrollerOffset, - scrollerTop, - scrollerBottom, - scrollerLeft, - rightOffset; + initComplete = false; // build selectionMarker and position absolute within the scroller $selectionMarker = $(document.createElement("div")).addClass("sidebarSelection"); @@ -94,24 +89,23 @@ define(function (require, exports, module) { // use relative postioning for clipping the selectionMarker within the scrollElement $scrollerElement.css("position", "relative"); - // build selectionTriangle and position fixed to the windwo + // build selectionTriangle and position fixed to the window $selectionTriangle = $(document.createElement("div")).addClass("sidebarSelectionTriangle"); $fileSection.append($selectionTriangle); selectedClassName = "." + (selectedClassName || "selected"); var updateSelectionTriangle = function () { - scrollerOffset = $scrollerElement.offset(); - scrollerTop = scrollerOffset.top; - scrollerBottom = scrollerTop + $scrollerElement[0].clientHeight; - scrollerLeft = scrollerOffset.left; - - triangleTop = $selectionMarker.offset().top; - triangleHeight = $selectionTriangle.outerHeight(); - triangleOffsetYBy = $selectionMarker.height() / 2; - triangleClipOffsetYBy = Math.floor(($selectionMarker.height() - triangleHeight) / 2); - triangleBottom = triangleTop + triangleHeight + triangleClipOffsetYBy; - + var scrollerOffset = $scrollerElement.offset(), + scrollerTop = scrollerOffset.top, + scrollerBottom = scrollerTop + $scrollerElement.get(0).clientHeight, + scrollerLeft = scrollerOffset.left, + triangleTop = $selectionMarker.offset().top, + triangleHeight = $selectionTriangle.outerHeight(), + triangleOffsetYBy = $selectionMarker.height() / 2, + triangleClipOffsetYBy = Math.floor(($selectionMarker.height() - triangleHeight) / 2), + triangleBottom = triangleTop + triangleHeight + triangleClipOffsetYBy; + $selectionTriangle.css("top", triangleTop + triangleOffsetYBy); $selectionTriangle.css("left", $fileSection.width() - $selectionTriangle.outerWidth()); @@ -128,19 +122,11 @@ define(function (require, exports, module) { $listItem = $listElement.find(selectedClassName).closest("li"); if ($listItem.length === 1) { - // list item position is relative to it's immediate parent UL - selectionMarkerTop = $listItem.position().top; - - // determine top position relative to scroller by sum of nested list items - $.each($listItem.parentsUntil($scrollerElement, "li"), function (index, ancestor) { - selectionMarkerTop += $(ancestor).position().top; - }); - - // offset by current scroll position - selectionMarkerTop += $scrollerElement[0].scrollTop; + // list item position is relative to scroller + var selectionMarkerTop = $listItem.offset().top - $scrollerElement.offset().top + $scrollerElement.get(0).scrollTop; // force selection width to match scroller - $selectionMarker.width($scrollerElement[0].scrollWidth); + $selectionMarker.width($scrollerElement.get(0).scrollWidth); // move the selectionMarker position to align with the list item $selectionMarker.css("top", selectionMarkerTop); @@ -149,6 +135,21 @@ define(function (require, exports, module) { updateSelectionTriangle(); $selectionTriangle.show(); + + // scroll to the initial selection + if (!initComplete) { + // fully scroll to the selectionMarker if it's not initially in the viewport + var scrollerElement = $scrollerElement.get(0), + scrollerHeight = $scrollerElement.height(), + currentScrollBottom = scrollerElement.scrollTop + scrollerHeight, + scrollTop = Math.max(0, selectionMarkerTop + $selectionMarker.height() - scrollerHeight); + + if (selectionMarkerTop > currentScrollBottom) { + scrollerElement.scrollTop = scrollTop; + } + + initComplete = true; + } } else { // hide the selection marker when no selection is found $selectionTriangle.hide(); @@ -161,6 +162,11 @@ define(function (require, exports, module) { // update immediately updateSelectionMarker(); + + // update clipping when the window resizes + $(window).on("resize", function () { + setTimeout(updateSelectionTriangle, 0); + }); } // Define public API From ba3c5f8aacac9390826e9b202671bca2a5681f28 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 20 Apr 2012 16:45:52 -0700 Subject: [PATCH 6/7] Update scrollTop for every selection change. Address comments. --- src/styles/brackets_variables.less | 5 ++-- src/utils/ViewUtils.js | 37 +++++++++++++----------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/styles/brackets_variables.less b/src/styles/brackets_variables.less index 9e45a9da612..b69f0d4199d 100644 --- a/src/styles/brackets_variables.less +++ b/src/styles/brackets_variables.less @@ -6,8 +6,7 @@ /* Common font sizes */ @title-font-size: 18px; // headings such as the editor titlebar @label-font-size: 14px; // labels on buttons, menubar, etc. +@menu-item-font-size: 13px; // individual menu items /* CSS triangle */ -@triangle-size: 10px; - -@menu-item-font-size: 13px; // individual menu items +@triangle-size: 10px; \ No newline at end of file diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index 1b5b90ce873..2bcb9d2bbf1 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -63,7 +63,7 @@ define(function (require, exports, module) { * Within a scrolling DOMElement, creates and positions a styled selection * div to align a single selected list item from a ul list element. * - * Assumtions: + * Assumptions: * - scrollElement is a child of the #file-section div * - ul list element fires a "selectionChanged" event after the * selectedClassName is assigned to a new list item @@ -73,11 +73,9 @@ define(function (require, exports, module) { */ function sidebarList($scrollerElement, selectedClassName) { var $listElement = $scrollerElement.find("ul"), - $listItem, $selectionMarker, $selectionTriangle, - $fileSection = $("#file-section"), - initComplete = false; + $fileSection = $("#file-section"); // build selectionMarker and position absolute within the scroller $selectionMarker = $(document.createElement("div")).addClass("sidebarSelection"); @@ -119,7 +117,7 @@ define(function (require, exports, module) { var updateSelectionMarker = function () { // find the selected list item - $listItem = $listElement.find(selectedClassName).closest("li"); + var $listItem = $listElement.find(selectedClassName).closest("li"); if ($listItem.length === 1) { // list item position is relative to scroller @@ -135,20 +133,19 @@ define(function (require, exports, module) { updateSelectionTriangle(); $selectionTriangle.show(); + + // fully scroll to the selectionMarker if it's not initially in the viewport + var scrollerElement = $scrollerElement.get(0), + scrollerHeight = $scrollerElement.height(), + selectionMarkerHeight = $selectionMarker.height(), + selectionMarkerBottom = selectionMarkerTop + selectionMarkerHeight, + currentScrollBottom = scrollerElement.scrollTop + scrollerHeight; - // scroll to the initial selection - if (!initComplete) { - // fully scroll to the selectionMarker if it's not initially in the viewport - var scrollerElement = $scrollerElement.get(0), - scrollerHeight = $scrollerElement.height(), - currentScrollBottom = scrollerElement.scrollTop + scrollerHeight, - scrollTop = Math.max(0, selectionMarkerTop + $selectionMarker.height() - scrollerHeight); - - if (selectionMarkerTop > currentScrollBottom) { - scrollerElement.scrollTop = scrollTop; - } - - initComplete = true; + // update scrollTop to reveal the selected list item + if (selectionMarkerTop > currentScrollBottom) { + scrollerElement.scrollTop = Math.max(0, selectionMarkerTop + selectionMarkerHeight - scrollerHeight); + } else if (selectionMarkerBottom < scrollerElement.scrollTop) { + scrollerElement.scrollTop = selectionMarkerTop; } } else { // hide the selection marker when no selection is found @@ -164,9 +161,7 @@ define(function (require, exports, module) { updateSelectionMarker(); // update clipping when the window resizes - $(window).on("resize", function () { - setTimeout(updateSelectionTriangle, 0); - }); + $(window).on("resize", updateSelectionTriangle); } // Define public API From 6c154f71a8e4e62467dcc62d272a5f8941fb599f Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 20 Apr 2012 17:07:02 -0700 Subject: [PATCH 7/7] Fix scroller height and reveal edge cases. --- src/utils/ViewUtils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index 2bcb9d2bbf1..d1cb90a3063 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -136,15 +136,15 @@ define(function (require, exports, module) { // fully scroll to the selectionMarker if it's not initially in the viewport var scrollerElement = $scrollerElement.get(0), - scrollerHeight = $scrollerElement.height(), + scrollerHeight = scrollerElement.clientHeight, selectionMarkerHeight = $selectionMarker.height(), selectionMarkerBottom = selectionMarkerTop + selectionMarkerHeight, currentScrollBottom = scrollerElement.scrollTop + scrollerHeight; // update scrollTop to reveal the selected list item - if (selectionMarkerTop > currentScrollBottom) { + if (selectionMarkerTop >= currentScrollBottom) { scrollerElement.scrollTop = Math.max(0, selectionMarkerTop + selectionMarkerHeight - scrollerHeight); - } else if (selectionMarkerBottom < scrollerElement.scrollTop) { + } else if (selectionMarkerBottom <= scrollerElement.scrollTop) { scrollerElement.scrollTop = selectionMarkerTop; } } else {