Changeset 27827 in webkit


Ignore:
Timestamp:
Nov 15, 2007 4:07:44 PM (16 years ago)
Author:
timothy@apple.com
Message:

Reviewed by Adam.

Bug 16005: Hovering in the breadcrumbs causes jumpy behavior
http://bugs.webkit.org/show_bug.cgi?id=16005

Hovering over cumbs no longer exposes new crumbs. Clicking on a collapsed
crumb will expose as many hidden crumbs as possible to the user. Also crumbs
that have ID attributes will compact to the ID over the tag name.

  • page/inspector/DocumentPanel.js:
Location:
trunk/WebCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r27826 r27827  
     12007-11-15  Timothy Hatcher  <timothy@apple.com>
     2
     3        Reviewed by Adam.
     4
     5        Bug 16005: Hovering in the breadcrumbs causes jumpy behavior
     6        http://bugs.webkit.org/show_bug.cgi?id=16005
     7
     8        Hovering over cumbs no longer exposes new crumbs. Clicking on a collapsed
     9        crumb will expose as many hidden crumbs as possible to the user. Also crumbs
     10        that have ID attributes will compact to the ID over the tag name.
     11
     12        * page/inspector/DocumentPanel.js:
     13
    1142007-11-15  Anders Carlsson  <andersca@apple.com>
    215
  • trunk/WebCore/page/inspector/DocumentPanel.js

    r27789 r27827  
    225225        var panel = this;
    226226        var selectCrumbFunction = function(event) {
    227             // Clicking a dimmed crumb or double clicking (event.detail >= 2)
    228             // will change the root node in addition to the focused node.
    229             if (event.detail >= 2 || event.currentTarget.hasStyleClass("dimmed"))
    230                 panel.rootDOMNode = event.currentTarget.representedObject.parentNode;
    231             panel.focusedDOMNode = event.currentTarget.representedObject;
     227            var crumb = event.currentTarget;
     228            if (crumb.hasStyleClass("collapsed")) {
     229                // Clicking a collapsed crumb will expose the hidden crumbs.
     230                if (crumb === panel.views.dom.innerCrumbsElement.firstChild) {
     231                    // If the focused crumb is the first child, pick the farthest crumb
     232                    // that is still hidden. This allows the user to expose every crumb.
     233                    var currentCrumb = crumb;
     234                    while (currentCrumb) {
     235                        var hidden = currentCrumb.hasStyleClass("hidden");
     236                        var collapsed = currentCrumb.hasStyleClass("collapsed");
     237                        if (!hidden && !collapsed)
     238                            break;
     239                        crumb = currentCrumb;
     240                        currentCrumb = currentCrumb.nextSibling;
     241                    }
     242                }
     243
     244                panel.updateBreadcrumbSizes(crumb);
     245            } else {
     246                // Clicking a dimmed crumb or double clicking (event.detail >= 2)
     247                // will change the root node in addition to the focused node.
     248                if (event.detail >= 2 || crumb.hasStyleClass("dimmed"))
     249                    panel.rootDOMNode = crumb.representedObject.parentNode;
     250                panel.focusedDOMNode = crumb.representedObject;
     251            }
     252
    232253            event.preventDefault();
    233254        };
     
    240261                delete panel.mouseOutTimeout;
    241262            }
    242 
    243             panel.updateBreadcrumbSizes(event.currentTarget);
    244263        };
    245264
     
    257276            };
    258277
    259             panel.mouseOutTimeout = setTimeout(timeoutFunction, 250);
     278            panel.mouseOutTimeout = setTimeout(timeoutFunction, 500);
    260279        };
    261280
     
    285304                    crumb.appendChild(nameElement);
    286305
    287                     var selectorElement = document.createElement("span");
    288                     selectorElement.className = "extra";
    289                     crumb.appendChild(selectorElement);
    290 
    291                     var value = current.getAttribute("id");
    292                     if (value) {
    293                         var part = "#" + value;
     306                    var idAttribute = current.getAttribute("id");
     307                    if (idAttribute) {
     308                        var idElement = document.createElement("span");
     309                        crumb.appendChild(idElement);
     310
     311                        var part = "#" + idAttribute;
    294312                        crumbTitle += part;
    295                         selectorElement.appendChild(document.createTextNode(part));
     313                        idElement.appendChild(document.createTextNode(part));
     314
     315                        // Mark the name as extra, since the ID is more important.
     316                        nameElement.className = "extra";
    296317                    }
    297318
    298                     value = current.getAttribute("class");
    299                     if (value) {
    300                         var classes = value.split(/\s+/);
     319                    var classAttribute = current.getAttribute("class");
     320                    if (classAttribute) {
     321                        var classes = classAttribute.split(/\s+/);
    301322                        var foundClasses = {};
    302                         for (var i = 0; i < classes.length; ++i) {
    303                             value = classes[i];
    304                             if (value && !(value in foundClasses)) {
    305                                 var part = "." + value;
    306                                 crumbTitle += part;
    307                                 selectorElement.appendChild(document.createTextNode(part));
    308                                 foundClasses[value] = true;
     323
     324                        if (classes.length) {
     325                            var classesElement = document.createElement("span");
     326                            classesElement.className = "extra";
     327                            crumb.appendChild(classesElement);
     328
     329                            for (var i = 0; i < classes.length; ++i) {
     330                                var className = classes[i];
     331                                if (className && !(className in foundClasses)) {
     332                                    var part = "." + className;
     333                                    crumbTitle += part;
     334                                    classesElement.appendChild(document.createTextNode(part));
     335                                    foundClasses[className] = true;
     336                                }
    309337                            }
    310338                        }
     
    352380    },
    353381
    354     updateBreadcrumbSizes: function(hoveredCrumb)
     382    updateBreadcrumbSizes: function(focusedCrumb)
    355383    {
    356384        var crumbs = this.views.dom.innerCrumbsElement;
     
    362390            return; // The cumbs are not visible yet, do nothing.
    363391
     392        // A Zero index is the right most child crumb in the breadcrumb.
     393        var selectedIndex = 0;
     394        var focusedIndex = 0;
    364395        var selectedCrumb;
    365396
    366         // Remove any styles that affect size before deciding to shorten any crumbs.
     397        var i = 0;
    367398        var crumb = crumbs.firstChild;
    368399        while (crumb) {
    369             if (!selectedCrumb && crumb.hasStyleClass("selected"))
     400            // Find the selected crumb and index.
     401            if (!selectedCrumb && crumb.hasStyleClass("selected")) {
    370402                selectedCrumb = crumb;
     403                selectedIndex = i;
     404            }
     405
     406            // Find the focused crumb index.
     407            if (crumb === focusedCrumb)
     408                focusedIndex = i;
     409
     410            // Remove any styles that affect size before
     411            // deciding to shorten any crumbs.
    371412            if (crumb !== crumbs.lastChild)
    372413                crumb.removeStyleClass("start");
    373414            if (crumb !== crumbs.firstChild)
    374415                crumb.removeStyleClass("end");
     416
    375417            crumb.removeStyleClass("compact");
    376418            crumb.removeStyleClass("collapsed");
    377419            crumb.removeStyleClass("hidden");
     420
    378421            crumb = crumb.nextSibling;
     422            ++i;
    379423        }
    380424
     
    395439            return; // No need to compact the crumbs, they all fit at full size.
    396440
    397         function makeCrumbsSmaller(shrinkingFunction, significantCrumb)
     441        var BothSides = 0;
     442        var AncestorSide = -1;
     443        var ChildSide = 1;
     444
     445        function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb)
    398446        {
    399447            if (!significantCrumb)
    400                 significantCrumb = (hoveredCrumb || selectedCrumb);
    401 
    402             // Look for the significant crumb in reverse order, so if we don't find it the index will be Zero.
    403             // A Zero index is the right most visual position in the breadcrumb.
    404             for (var significantIndex = (crumbs.childNodes.length - 1); significantIndex >= 0; --significantIndex)
    405                 if (crumbs.childNodes[significantIndex] === significantCrumb)
    406                     break;
    407 
    408             // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
    409             // fit in the crumbsContainer or we run out of crumbs to shrink. Crumbs are shrunk
    410             // in order of descending distance from the signifcant crumb, with a tie going
    411             // to crumbs on the left.
    412 
    413             var startIndex = 0;
    414             var endIndex = crumbs.childNodes.length - 1;
    415             while (startIndex != significantIndex || endIndex != significantIndex) {
    416                 var startDistance = significantIndex - startIndex;
    417                 var endDistance = endIndex - significantIndex;
    418                 if (startDistance > endDistance) {
    419                     var shrinkCrumb = crumbs.childNodes[startIndex];
    420                     ++startIndex;
    421                 } else {
    422                     var shrinkCrumb = crumbs.childNodes[endIndex];
    423                     --endIndex;
     448                significantCrumb = (focusedCrumb || selectedCrumb);
     449
     450            if (significantCrumb === selectedCrumb)
     451                var significantIndex = selectedIndex;
     452            else if (significantCrumb === focusedCrumb)
     453                var significantIndex = focusedIndex;
     454            else {
     455                var significantIndex = 0;
     456                for (var i = 0; i < crumbs.childNodes.length; ++i) {
     457                    if (crumbs.childNodes[i] === significantCrumb) {
     458                        significantIndex = i;
     459                        break;
     460                    }
    424461                }
    425 
     462            }
     463
     464            function shrinkCrumbAtIndex(index)
     465            {
     466                var shrinkCrumb = crumbs.childNodes[index];
    426467                if (shrinkCrumb && shrinkCrumb !== significantCrumb)
    427468                    shrinkingFunction(shrinkCrumb);
    428 
    429469                if (crumbsAreSmallerThanContainer())
    430470                    return true; // No need to compact the crumbs more.
     471                return false;
     472            }
     473
     474            // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs
     475            // fit in the crumbsContainer or we run out of crumbs to shrink.
     476            if (direction) {
     477                // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb.
     478                var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1);
     479                while (index !== significantIndex) {
     480                    if (shrinkCrumbAtIndex(index))
     481                        return true;
     482                    index += (direction > 0 ? 1 : -1);
     483                }
     484            } else {
     485                // Crumbs are shrunk in order of descending distance from the signifcant crumb,
     486                // with a tie going to child crumbs.
     487                var startIndex = 0;
     488                var endIndex = crumbs.childNodes.length - 1;
     489                while (startIndex != significantIndex || endIndex != significantIndex) {
     490                    var startDistance = significantIndex - startIndex;
     491                    var endDistance = endIndex - significantIndex;
     492                    if (startDistance >= endDistance)
     493                        var index = startIndex++;
     494                    else
     495                        var index = endIndex--;
     496                    if (shrinkCrumbAtIndex(index))
     497                        return true;
     498                }
    431499            }
    432500
     
    486554        }
    487555
     556        function compact(crumb)
     557        {
     558            if (crumb.hasStyleClass("hidden"))
     559                return;
     560            crumb.addStyleClass("compact");
     561        }
     562
     563        function collapse(crumb, dontCoalesce)
     564        {
     565            if (crumb.hasStyleClass("hidden"))
     566                return;
     567            crumb.addStyleClass("collapsed");
     568            crumb.removeStyleClass("compact");
     569            if (!dontCoalesce)
     570                coalesceCollapsedCrumbs();
     571        }
     572
     573        function compactDimmed(crumb)
     574        {
     575            if (crumb.hasStyleClass("dimmed"))
     576                compact(crumb);
     577        }
     578
    488579        function collapseDimmed(crumb)
    489580        {
    490             if (crumb.hasStyleClass("dimmed")) {
    491                 crumb.addStyleClass("collapsed");
    492                 coalesceCollapsedCrumbs();
    493             }
    494         }
    495 
    496         // Prefer collapsing the dimmed crumbs first, only if we don't have a hovered crumb.
    497         if (!hoveredCrumb && makeCrumbsSmaller(collapseDimmed))
    498             return; // No need to compact the crumbs more.
    499 
    500         // Try compacting long crumbs next. Using the selected crumb as the significant crumbs makes
    501         // hovering more predicable and less jumpy.
    502         if (makeCrumbsSmaller(function(crumb) { crumb.addStyleClass("compact") }, selectedCrumb))
    503             return; // No need to compact the crumbs more.
    504 
    505         // Since we are still too large and we avoided compacting the selected crumb in the last step,
    506         // try compacting the selected crumb now.
    507         if (selectedCrumb) {
    508             selectedCrumb.addStyleClass("compact");           
    509             if (crumbsAreSmallerThanContainer())
    510                 return; // No need to compact the crumbs more.
    511         }
    512 
    513         // Nothing else has worked, try collapsing crumbs.
    514         if (makeCrumbsSmaller(function(crumb) { crumb.addStyleClass("collapsed"); coalesceCollapsedCrumbs(); }))
    515             return; // No need to compact the crumbs more.
     581            if (crumb.hasStyleClass("dimmed"))
     582                collapse(crumb);
     583        }
     584
     585        if (!focusedCrumb) {
     586            // When not focused on a crumb we can be biased and collapse less important
     587            // crumbs that the user might not care much about.
     588
     589            // Compact child crumbs.
     590            if (makeCrumbsSmaller(compact, ChildSide))
     591                return;
     592
     593            // Collapse child crumbs.
     594            if (makeCrumbsSmaller(collapse, ChildSide))
     595                return;
     596
     597            // Compact dimmed ancestor crumbs.
     598            if (makeCrumbsSmaller(compactDimmed, AncestorSide))
     599                return;
     600
     601            // Collapse dimmed ancestor crumbs.
     602            if (makeCrumbsSmaller(collapseDimmed, AncestorSide))
     603                return;
     604        }
     605
     606        // Compact ancestor crumbs, or from both sides if focused.
     607        if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide)))
     608            return;
     609
     610        // Collapse ancestor crumbs, or from both sides if focused.
     611        if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide)))
     612            return;
    516613
    517614        if (!selectedCrumb)
    518615            return;
    519616
    520         selectedCrumb.addStyleClass("collapsed");
     617        // Compact the selected crumb.
     618        compact(selectedCrumb);
     619        if (crumbsAreSmallerThanContainer())
     620            return;
     621
     622        // Collapse the selected crumb as a last resort. Pass true to prevent coalescing.
     623        collapse(selectedCrumb, true);
    521624    },
    522625
     
    616719
    617720            this.updateTreeSelection();
     721            this.updateBreadcrumbSizes();
    618722
    619723            event.preventDefault();
Note: See TracChangeset for help on using the changeset viewer.