browser/components/customizableui/content/panelUI.xml

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

     1 <?xml version="1.0"?>
     2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
     3    - License, v. 2.0. If a copy of the MPL was not distributed with this
     4    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     6 <bindings id="browserPanelUIBindings"
     7           xmlns="http://www.mozilla.org/xbl"
     8           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
     9           xmlns:xbl="http://www.mozilla.org/xbl">
    11   <binding id="panelmultiview">
    12     <resources>
    13       <stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
    14     </resources>
    15     <content>
    16       <xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,viewtype,transitioning">
    17         <xul:stack anonid="viewStack" xbl:inherits="viewtype" viewtype="main" class="panel-viewstack">
    18           <xul:vbox anonid="mainViewContainer" class="panel-mainview"/>
    20           <!-- Used to capture click events over the PanelUI-mainView if we're in
    21                subview mode. That way, any click on the PanelUI-mainView causes us
    22                to revert to the mainView mode, whereupon PanelUI-click-capture then
    23                allows click events to go through it. -->
    24           <xul:vbox anonid="clickCapturer" class="panel-clickcapturer"/>
    26           <!-- We manually set display: none (via a CSS attribute selector) on the
    27                subviews that are not being displayed. We're using this over a deck
    28                because a deck assumes the size of its largest child, regardless of
    29                whether or not it is shown. That's not good for our case, since we
    30                want to allow each subview to be uniquely sized. -->
    31           <xul:vbox anonid="subViews" class="panel-subviews" xbl:inherits="panelopen">
    32             <children includes="panelview"/>
    33           </xul:vbox>
    34         </xul:stack>
    35       </xul:box>
    36     </content>
    37     <implementation implements="nsIDOMEventListener">
    38       <field name="_clickCapturer" readonly="true">
    39         document.getAnonymousElementByAttribute(this, "anonid", "clickCapturer");
    40       </field>
    41       <field name="_viewContainer" readonly="true">
    42         document.getAnonymousElementByAttribute(this, "anonid", "viewContainer");
    43       </field>
    44       <field name="_mainViewContainer" readonly="true">
    45         document.getAnonymousElementByAttribute(this, "anonid", "mainViewContainer");
    46       </field>
    47       <field name="_subViews" readonly="true">
    48         document.getAnonymousElementByAttribute(this, "anonid", "subViews");
    49       </field>
    50       <field name="_viewStack" readonly="true">
    51         document.getAnonymousElementByAttribute(this, "anonid", "viewStack");
    52       </field>
    53       <field name="_panel" readonly="true">
    54         this.parentNode;
    55       </field>
    57       <field name="_currentSubView">null</field>
    58       <field name="_anchorElement">null</field>
    59       <field name="_mainViewHeight">0</field>
    60       <field name="_subViewObserver">null</field>
    61       <field name="__transitioning">false</field>
    62       <field name="_ignoreMutations">false</field>
    64       <property name="showingSubView" readonly="true"
    65                 onget="return this._viewStack.getAttribute('viewtype') == 'subview'"/>
    66       <property name="_mainViewId" onget="return this.getAttribute('mainViewId');" onset="this.setAttribute('mainViewId', val); return val;"/>
    67       <property name="_mainView" readonly="true"
    68                 onget="return this._mainViewId ? document.getElementById(this._mainViewId) : null;"/>
    69       <property name="showingSubViewAsMainView" readonly="true"
    70                 onget="return this.getAttribute('mainViewIsSubView') == 'true'"/>
    72       <property name="ignoreMutations">
    73         <getter>
    74           return this._ignoreMutations;
    75         </getter>
    76         <setter><![CDATA[
    77           this._ignoreMutations = val;
    78           if (!val && this._panel.state == "open") {
    79             if (this.showingSubView) {
    80               this._syncContainerWithSubView();
    81             } else {
    82               this._syncContainerWithMainView();
    83             }
    84           }
    85         ]]></setter>
    86       </property>
    88       <property name="_transitioning">
    89         <getter>
    90           return this.__transitioning;
    91         </getter>
    92         <setter><![CDATA[
    93           this.__transitioning = val;
    94           if (val) {
    95             this.setAttribute("transitioning", "true");
    96           } else {
    97             this.removeAttribute("transitioning");
    98           }
    99         ]]></setter>
   100       </property>
   101       <constructor><![CDATA[
   102         this._clickCapturer.addEventListener("click", this);
   103         this._panel.addEventListener("popupshowing", this);
   104         this._panel.addEventListener("popupshown", this);
   105         this._panel.addEventListener("popuphidden", this);
   106         this._subViews.addEventListener("overflow", this);
   107         this._mainViewContainer.addEventListener("overflow", this);
   109         // Get a MutationObserver ready to react to subview size changes. We
   110         // only attach this MutationObserver when a subview is being displayed.
   111         this._subViewObserver =
   112           new MutationObserver(this._syncContainerWithSubView.bind(this));
   113         this._mainViewObserver =
   114           new MutationObserver(this._syncContainerWithMainView.bind(this));
   116         this._mainViewContainer.setAttribute("panelid",
   117                                              this._panel.id);
   119         if (this._mainView) {
   120           this.setMainView(this._mainView);
   121         }
   122         this.setAttribute("viewtype", "main");
   123       ]]></constructor>
   125       <destructor><![CDATA[
   126         if (this._mainView) {
   127           this._mainView.removeAttribute("mainview");
   128         }
   129         this._mainViewObserver.disconnect();
   130         this._subViewObserver.disconnect();
   131         this._panel.removeEventListener("popupshowing", this);
   132         this._panel.removeEventListener("popupshown", this);
   133         this._panel.removeEventListener("popuphidden", this);
   134         this._subViews.removeEventListener("overflow", this);
   135         this._mainViewContainer.removeEventListener("overflow", this);
   136         this._clickCapturer.removeEventListener("click", this);
   137       ]]></destructor>
   139       <method name="setMainView">
   140         <parameter name="aNewMainView"/>
   141         <body><![CDATA[
   142         if (this._mainView) {
   143           this._mainViewObserver.disconnect();
   144           this._subViews.appendChild(this._mainView);
   145           this._mainView.removeAttribute("mainview");
   146         }
   147         this._mainViewId = aNewMainView.id;
   148         aNewMainView.setAttribute("mainview", "true");
   149         this._mainViewContainer.appendChild(aNewMainView);
   150         ]]></body>
   151       </method>
   153       <method name="showMainView">
   154         <body><![CDATA[
   155           if (this.showingSubView) {
   156             let viewNode = this._currentSubView;
   157             let evt = document.createEvent("CustomEvent");
   158             evt.initCustomEvent("ViewHiding", true, true, viewNode);
   159             viewNode.dispatchEvent(evt);
   161             viewNode.removeAttribute("current");
   162             this._currentSubView = null;
   164             this._subViewObserver.disconnect();
   166             this._transitioning = true;
   168             this._viewContainer.addEventListener("transitionend", function trans() {
   169               this._viewContainer.removeEventListener("transitionend", trans);
   170               this._transitioning = false;
   171             }.bind(this));
   172             this._viewContainer.style.height = this._mainViewHeight + "px";
   174             this.setAttribute("viewtype", "main");
   175           }
   177           this._shiftMainView();
   178         ]]></body>
   179       </method>
   181       <method name="showSubView">
   182         <parameter name="aViewId"/>
   183         <parameter name="aAnchor"/>
   184         <body><![CDATA[
   185           let viewNode = this.querySelector("#" + aViewId);
   186           viewNode.setAttribute("current", true);
   187           // Emit the ViewShowing event so that the widget definition has a chance
   188           // to lazily populate the subview with things.
   189           let evt = document.createEvent("CustomEvent");
   190           evt.initCustomEvent("ViewShowing", true, true, viewNode);
   191           viewNode.dispatchEvent(evt);
   192           if (evt.defaultPrevented) {
   193             return;
   194           }
   196           this._currentSubView = viewNode;
   198           // Now we have to transition the panel. There are a few parts to this:
   199           //
   200           // 1) The main view content gets shifted so that the center of the anchor
   201           //    node is at the left-most edge of the panel.
   202           // 2) The subview deck slides in so that it takes up almost all of the
   203           //    panel.
   204           // 3) If the subview is taller then the main panel contents, then the panel
   205           //    must grow to meet that new height. Otherwise, it must shrink.
   206           //
   207           // All three of these actions make use of CSS transformations, so they
   208           // should all occur simultaneously.
   209           this.setAttribute("viewtype", "subview");
   210           this._shiftMainView(aAnchor);
   212           this._mainViewHeight = this._viewStack.clientHeight;
   214           this._transitioning = true;
   215           this._viewContainer.addEventListener("transitionend", function trans() {
   216             this._viewContainer.removeEventListener("transitionend", trans);
   217             this._transitioning = false;
   218           }.bind(this));
   220           let newHeight = this._heightOfSubview(viewNode, this._subViews);
   221           this._viewContainer.style.height = newHeight + "px";
   223           this._subViewObserver.observe(viewNode, {
   224             attributes: true,
   225             characterData: true,
   226             childList: true,
   227             subtree: true
   228           });
   229         ]]></body>
   230       </method>
   232       <method name="_shiftMainView">
   233         <parameter name="aAnchor"/>
   234         <body><![CDATA[
   235           if (aAnchor) {
   236             // We need to find the edge of the anchor, relative to the main panel.
   237             // Then we need to add half the width of the anchor. This is the target
   238             // that we need to transition to.
   239             let anchorRect = aAnchor.getBoundingClientRect();
   240             let mainViewRect = this._mainViewContainer.getBoundingClientRect();
   241             let center = aAnchor.clientWidth / 2;
   242             let direction = aAnchor.ownerDocument.defaultView.getComputedStyle(aAnchor, null).direction;
   243             let edge, target;
   244             if (direction == "ltr") {
   245               edge = anchorRect.left - mainViewRect.left;
   246               target = "-" + (edge + center);
   247             } else {
   248               edge = mainViewRect.right - anchorRect.right;
   249               target = edge + center;
   250             }
   251             this._mainViewContainer.style.transform = "translateX(" + target + "px)";
   252             aAnchor.setAttribute("panel-multiview-anchor", true);
   253           } else {
   254             this._mainViewContainer.style.transform = "";
   255             if (this.anchorElement)
   256               this.anchorElement.removeAttribute("panel-multiview-anchor");
   257           }
   258           this.anchorElement = aAnchor;
   259         ]]></body>
   260       </method>
   262       <method name="handleEvent">
   263         <parameter name="aEvent"/>
   264         <body><![CDATA[
   265           if (aEvent.type.startsWith("popup") && aEvent.target != this._panel) {
   266             // Shouldn't act on e.g. context menus being shown from within the panel.
   267             return;
   268           }
   269           switch (aEvent.type) {
   270             case "click":
   271               if (aEvent.originalTarget == this._clickCapturer) {
   272                 this.showMainView();
   273               }
   274               break;
   275             case "overflow":
   276               if (aEvent.target.localName == "vbox") {
   277                 // Resize the right view on the next tick.
   278                 if (this.showingSubView) {
   279                   setTimeout(this._syncContainerWithSubView.bind(this), 0);
   280                 } else if (!this.transitioning) {
   281                   setTimeout(this._syncContainerWithMainView.bind(this), 0);
   282                 }
   283               }
   284               break;
   285             case "popupshowing":
   286               this.setAttribute("panelopen", "true");
   287               // Bug 941196 - The panel can get taller when opening a subview. Disabling
   288               // autoPositioning means that the panel won't jump around if an opened
   289               // subview causes the panel to exceed the dimensions of the screen in the
   290               // direction that the panel originally opened in. This property resets
   291               // every time the popup closes, which is why we have to set it each time.
   292               this._panel.autoPosition = false;
   293               this._syncContainerWithMainView();
   295               this._mainViewObserver.observe(this._mainView, {
   296                 attributes: true,
   297                 characterData: true,
   298                 childList: true,
   299                 subtree: true
   300               });
   302               break;
   303             case "popupshown":
   304               this._setMaxHeight();
   305               break;
   306             case "popuphidden":
   307               this.removeAttribute("panelopen");
   308               this._mainView.style.removeProperty("height");
   309               this.showMainView();
   310               this._mainViewObserver.disconnect();
   311               break;
   312           }
   313         ]]></body>
   314       </method>
   316       <method name="_shouldSetHeight">
   317         <body><![CDATA[
   318           return this.getAttribute("nosubviews") != "true";
   319         ]]></body>
   320       </method>
   322       <method name="_setMaxHeight">
   323         <body><![CDATA[
   324           if (!this._shouldSetHeight())
   325             return;
   327           // Ignore the mutation that'll fire when we set the height of
   328           // the main view.
   329           this.ignoreMutations = true;
   330           this._mainView.style.height =
   331             this.getBoundingClientRect().height + "px";
   332           this.ignoreMutations = false;
   333         ]]></body>
   334       </method>
   335       <method name="_syncContainerWithSubView">
   336         <body><![CDATA[
   337           // Check that this panel is still alive:
   338           if (!this._panel || !this._panel.parentNode) {
   339             return;
   340           }
   342           if (!this.ignoreMutations && this.showingSubView) {
   343             let newHeight = this._heightOfSubview(this._currentSubView, this._subViews);
   344             this._viewContainer.style.height = newHeight + "px";
   345           }
   346         ]]></body>
   347       </method>
   348       <method name="_syncContainerWithMainView">
   349         <body><![CDATA[
   350           // Check that this panel is still alive:
   351           if (!this._panel || !this._panel.parentNode || !this._shouldSetHeight()) {
   352             return;
   353           }
   355           if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
   356             let height;
   357             if (this.showingSubViewAsMainView) {
   358               height = this._heightOfSubview(this._mainView);
   359             } else {
   360               height = this._mainView.scrollHeight;
   361             }
   362             this._viewContainer.style.height = height + "px";
   363           }
   364         ]]></body>
   365       </method>
   367       <method name="_heightOfSubview">
   368         <parameter name="aSubview"/>
   369         <parameter name="aContainerToCheck"/>
   370         <body><![CDATA[
   371           function getFullHeight(element) {
   372             //XXXgijs: unfortunately, scrollHeight rounds values, and there's no alternative
   373             // that works with overflow: auto elements. Fortunately for us,
   374             // we have exactly 1 (potentially) scrolling element in here (the subview body),
   375             // and rounding 1 value is OK - rounding more than 1 and adding them means we get
   376             // off-by-1 errors. Now we might be off by a subpixel, but we care less about that.
   377             // So, use scrollHeight *only* if the element is vertically scrollable.
   378             let height;
   379             let elementCS;
   380             if (element.scrollTopMax) {
   381               height = element.scrollHeight;
   382               // Bounding client rects include borders, scrollHeight doesn't:
   383               elementCS = win.getComputedStyle(element);
   384               height += parseFloat(elementCS.borderTopWidth) +
   385                         parseFloat(elementCS.borderBottomWidth);
   386             } else {
   387               height = element.getBoundingClientRect().height;
   388               if (height > 0) {
   389                 elementCS = win.getComputedStyle(element);
   390               }
   391             }
   392             if (elementCS) {
   393               // Include margins - but not borders or paddings because they
   394               // were dealt with above.
   395               height += parseFloat(elementCS.marginTop) + parseFloat(elementCS.marginBottom);
   396             }
   397             return height;
   398           }
   399           let win = aSubview.ownerDocument.defaultView;
   400           let body = aSubview.querySelector(".panel-subview-body");
   401           let height = getFullHeight(body || aSubview);
   402           if (body) {
   403             let header = aSubview.querySelector(".panel-subview-header");
   404             let footer = aSubview.querySelector(".panel-subview-footer");
   405             height += (header ? getFullHeight(header) : 0) +
   406                       (footer ? getFullHeight(footer) : 0);
   407           }
   408           if (aContainerToCheck) {
   409             let containerCS = win.getComputedStyle(aContainerToCheck);
   410             height += parseFloat(containerCS.paddingTop) + parseFloat(containerCS.paddingBottom);
   411           }
   412           return Math.round(height);
   413         ]]></body>
   414       </method>
   416     </implementation>
   417   </binding>
   419   <binding id="panelview">
   420     <implementation>
   421       <property name="panelMultiView" readonly="true">
   422         <getter><![CDATA[
   423           if (this.parentNode.localName != "panelmultiview") {
   424             return document.getBindingParent(this.parentNode);
   425           }
   427           return this.parentNode;
   428         ]]></getter>
   429       </property>
   430     </implementation>
   431   </binding>
   432 </bindings>

mercurial