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.

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

mercurial