browser/components/customizableui/src/PanelWideWidgetTracker.jsm

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     6 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
     8 this.EXPORTED_SYMBOLS = ["PanelWideWidgetTracker"];
    10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    11 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
    12   "resource:///modules/CustomizableUI.jsm");
    14 let gModuleName = "[PanelWideWidgetTracker]";
    15 #include logging.js
    17 let gPanel = CustomizableUI.AREA_PANEL;
    18 // We keep track of the widget placements for the panel locally:
    19 let gPanelPlacements = [];
    21 // All the wide widgets we know of:
    22 let gWideWidgets = new Set();
    23 // All the widgets we know of:
    24 let gSeenWidgets = new Set();
    26 let PanelWideWidgetTracker = {
    27   // Listeners used to validate panel contents whenever they change:
    28   onWidgetAdded: function(aWidgetId, aArea, aPosition) {
    29     if (aArea == gPanel) {
    30       gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
    31       let moveForward = this.shouldMoveForward(aWidgetId, aPosition);
    32       this.adjustWidgets(aWidgetId, moveForward);
    33     }
    34   },
    35   onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
    36     if (aArea == gPanel) {
    37       gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
    38       let moveForward = this.shouldMoveForward(aWidgetId, aNewPosition);
    39       this.adjustWidgets(aWidgetId, moveForward);
    40     }
    41   },
    42   onWidgetRemoved: function(aWidgetId, aPrevArea) {
    43     if (aPrevArea == gPanel) {
    44       gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
    45       let pos = gPanelPlacements.indexOf(aWidgetId);
    46       this.adjustWidgets(aWidgetId, false);
    47     }
    48   },
    49   onWidgetReset: function(aWidgetId) {
    50     gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
    51   },
    52   // Listener to keep abreast of any new nodes. We use the DOM one because
    53   // we need access to the actual node's classlist, so we can't use the ones above.
    54   // Furthermore, onWidgetCreated only fires for API-based widgets, not for XUL ones.
    55   onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer) {
    56     if (!gSeenWidgets.has(aNode.id)) {
    57       if (aNode.classList.contains(CustomizableUI.WIDE_PANEL_CLASS)) {
    58         gWideWidgets.add(aNode.id);
    59       }
    60       gSeenWidgets.add(aNode.id);
    61     }
    62   },
    63   // When widgets get destroyed, we remove them from our sets of stuff we care about:
    64   onWidgetDestroyed: function(aWidgetId) {
    65     gSeenWidgets.delete(aWidgetId);
    66     gWideWidgets.delete(aWidgetId);
    67   },
    68   shouldMoveForward: function(aWidgetId, aPosition) {
    69     let currentWidgetAtPosition = gPanelPlacements[aPosition + 1];
    70     let rv = gWideWidgets.has(currentWidgetAtPosition) && !gWideWidgets.has(aWidgetId);
    71     // We might now think we can move forward, but for that we need at least 2 more small
    72     // widgets to be present:
    73     if (rv) {
    74       let furtherWidgets = gPanelPlacements.slice(aPosition + 2);
    75       let realWidgets = 0;
    76       if (furtherWidgets.length >= 2) {
    77         while (furtherWidgets.length && realWidgets < 2) {
    78           let w = furtherWidgets.shift();
    79           if (!gWideWidgets.has(w) && this.checkWidgetStatus(w)) {
    80             realWidgets++;
    81           }
    82         }
    83       }
    84       if (realWidgets < 2) {
    85         rv = false;
    86       }
    87     }
    88     return rv;
    89   },
    90   adjustWidgets: function(aWidgetId, aMoveForwards) {
    91     if (this.adjusting) {
    92       return;
    93     }
    94     this.adjusting = true;
    95     let widgetsAffected = [w for (w of gPanelPlacements) if (gWideWidgets.has(w))];
    96     // If we're moving the wide widgets forwards (down/to the right in the panel)
    97     // we want to start with the last widgets. Otherwise we move widgets over other wide
    98     // widgets, which might mess up their order. Likewise, if moving backwards we should start with
    99     // the first widget and work our way down/right from there.
   100     let compareFn = aMoveForwards ? (function(a, b) a < b) : (function(a, b) a > b)
   101     widgetsAffected.sort(function(a, b) compareFn(gPanelPlacements.indexOf(a),
   102                                                   gPanelPlacements.indexOf(b)));
   103     for (let widget of widgetsAffected) {
   104       this.adjustPosition(widget, aMoveForwards);
   105     }
   106     this.adjusting = false;
   107   },
   108   // This function is called whenever an item gets moved in the menu panel. It
   109   // adjusts the position of widgets within the panel to prevent "gaps" between
   110   // wide widgets that could be filled up with single column widgets
   111   adjustPosition: function(aWidgetId, aMoveForwards) {
   112     // Make sure that there are n % columns = 0 narrow buttons before the widget.
   113     let placementIndex = gPanelPlacements.indexOf(aWidgetId);
   114     let prevSiblingCount = 0;
   115     let fixedPos = null;
   116     while (placementIndex--) {
   117       let thisWidgetId = gPanelPlacements[placementIndex];
   118       if (gWideWidgets.has(thisWidgetId)) {
   119         continue;
   120       }
   121       let widgetStatus = this.checkWidgetStatus(thisWidgetId);
   122       if (!widgetStatus) {
   123         continue;
   124       }
   125       if (widgetStatus == "public-only") {
   126         fixedPos = !fixedPos ? placementIndex : Math.min(fixedPos, placementIndex);
   127         prevSiblingCount = 0;
   128       } else {
   129         prevSiblingCount++;
   130       }
   131     }
   133     if (fixedPos !== null || prevSiblingCount % CustomizableUI.PANEL_COLUMN_COUNT) {
   134       let desiredPos = (fixedPos !== null) ? fixedPos : gPanelPlacements.indexOf(aWidgetId);
   135       let desiredChange = -(prevSiblingCount % CustomizableUI.PANEL_COLUMN_COUNT);
   136       if (aMoveForwards && fixedPos == null) {
   137         // +1 because otherwise we'd count ourselves:
   138         desiredChange = CustomizableUI.PANEL_COLUMN_COUNT + desiredChange + 1;
   139       }
   140       desiredPos += desiredChange;
   141       CustomizableUI.moveWidgetWithinArea(aWidgetId, desiredPos);
   142     }
   143   },
   145   /*
   146    * Check whether a widget id is actually known anywhere.
   147    * @returns false if the widget doesn't exist,
   148    *          "public-only" if it's not shown in private windows
   149    *          "real" if it does exist and is shown even in private windows
   150    */
   151   checkWidgetStatus: function(aWidgetId) {
   152     let widgetWrapper = CustomizableUI.getWidget(aWidgetId);
   153     // This widget might not actually exist:
   154     if (!widgetWrapper) {
   155       return false;
   156     }
   157     // This widget might still not actually exist:
   158     if (widgetWrapper.provider == CustomizableUI.PROVIDER_XUL &&
   159         widgetWrapper.instances.length == 0) {
   160       return false;
   161     }
   163     // Or it might only be there some of the time:
   164     if (widgetWrapper.provider == CustomizableUI.PROVIDER_API &&
   165         widgetWrapper.showInPrivateBrowsing === false) {
   166       return "public-only";
   167     }
   168     return "real";
   169   },
   171   init: function() {
   172     // Initialize our local placements copy and register the listener
   173     gPanelPlacements = CustomizableUI.getWidgetIdsInArea(gPanel);
   174     CustomizableUI.addListener(this);
   175   },
   176 };

mercurial