Home | History | Annotate | Download | only in gpu_internals
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 
      6 /**
      7  * @fileoverview This implements a tab control.
      8  *
      9  * An individual tab within a tab control is, unsurprisingly, a Tab.
     10  * Tabs must be explicitly added/removed from the control.
     11  *
     12  * Tab titles are based on the label attribute of each child:
     13  *
     14  * <div>
     15  * <div label='Tab 1'>Hello</div>
     16  * <div label='Tab 2'>World</div>
     17  * </div>
     18  *
     19  * Results in:
     20  *
     21  * ---------------
     22  * | Tab1 | Tab2 |
     23  * | ---------------------------------------
     24  * | Hello World |
     25  * -----------------------------------------
     26  *
     27  */
     28 cr.define('gpu', function() {
     29   /**
     30    * Creates a new tab element. A tab element is one of multiple tabs
     31    * within a TabControl.
     32    * @constructor
     33    * @param {Object=} opt_propertyBag Optional properties.
     34    * @extends {HTMLDivElement}
     35    */
     36   var Tab = cr.ui.define('div');
     37   Tab.prototype = {
     38     __proto__: HTMLDivElement.prototype,
     39 
     40     decorate: function() {
     41     }
     42   };
     43 
     44   /**
     45    * Title for the tab.
     46    * @type {String}
     47    */
     48   cr.defineProperty(Tab, 'label', cr.PropertyKind.ATTR);
     49 
     50   /**
     51    * Whether the item is selected.
     52    * @type {boolean}
     53    */
     54   cr.defineProperty(Tab, 'selected', cr.PropertyKind.BOOL_ATTR);
     55 
     56 
     57   /**
     58    * Creates a new tab button element in the tabstrip
     59    * @constructor
     60    * @param {Object=} opt_propertyBag Optional properties.
     61    * @extends {HTMLDivElement}
     62    */
     63   var TabButton = cr.ui.define('a');
     64   TabButton.prototype = {
     65     __proto__: HTMLAnchorElement.prototype,
     66 
     67     decorate: function() {
     68       this.classList.add('tab-button');
     69       this.onclick = function() {
     70         if (this.tab_)
     71           this.parentNode.parentNode.selectedTab = this.tab_;
     72       }.bind(this);
     73     },
     74     get tab() {
     75       return this.tab_;
     76     },
     77     set tab(tab) {
     78       if (this.tab_)
     79         throw Error('Cannot set tab once set.');
     80       this.tab_ = tab;
     81       this.tab_.addEventListener('titleChange', this.onTabChanged_.bind(this));
     82       this.tab_.addEventListener('selectedChange',
     83                                  this.onTabChanged_.bind(this));
     84       this.onTabChanged_();
     85     },
     86 
     87     onTabChanged_: function(e) {
     88       if (this.tab_) {
     89         this.textContent = this.tab_.label;
     90         this.selected = this.tab_.selected;
     91       }
     92     }
     93 
     94   };
     95 
     96   /**
     97    * Whether the TabButton is selected.
     98    * @type {boolean}
     99    */
    100   cr.defineProperty(TabButton, 'selected', cr.PropertyKind.BOOL_ATTR);
    101 
    102 
    103   /**
    104    * Creates a new tab control element.
    105    * @param {Object=} opt_propertyBag Optional properties.
    106    * @constructor
    107    * @extends {HTMLDivElement}
    108    */
    109   var TabControl = cr.ui.define('div');
    110   TabControl.prototype = {
    111     __proto__: HTMLDivElement.prototype,
    112 
    113     selectedTab_: null,
    114 
    115     /**
    116      * Initializes the tab control element.
    117      * Any child elements pre-existing on the element will become tabs.
    118      */
    119     decorate: function() {
    120       this.classList.add('tab-control');
    121 
    122       this.tabStrip_ = this.ownerDocument.createElement('div');
    123       this.tabStrip_.classList.add('tab-strip');
    124 
    125       this.tabs_ = this.ownerDocument.createElement('div');
    126       this.tabs_.classList.add('tabs');
    127 
    128       this.insertBefore(this.tabs_, this.firstChild);
    129       this.insertBefore(this.tabStrip_, this.firstChild);
    130 
    131       this.boundOnTabSelectedChange_ = this.onTabSelectedChange_.bind(this);
    132 
    133       // Reparent existing tabs to the tabs_ div.
    134       while (this.children.length > 2)
    135         this.addTab(this.children[2]);
    136     },
    137 
    138     /**
    139      * Adds an element to the tab control.
    140      */
    141     addTab: function(tab) {
    142       if (tab.parentNode == this.tabs_)
    143         throw Error('Tab is already part of this control.');
    144       if (!(tab instanceof Tab))
    145         throw Error('Provided element is not instanceof Tab.');
    146       this.tabs_.appendChild(tab);
    147 
    148       tab.addEventListener('selectedChange', this.boundOnTabSelectedChange_);
    149 
    150       var button = new TabButton();
    151       button.tab = tab;
    152       tab.tabStripButton_ = button;
    153 
    154       this.tabStrip_.appendChild(button);
    155 
    156       if (this.tabs_.length == 1)
    157         this.tabs_.children[0].selected = true;
    158     },
    159 
    160     /**
    161      * Removes a tab from the tab control.
    162      * changing the selected tab if needed.
    163      */
    164     removeTab: function(tab) {
    165       if (tab.parentNode != this.tabs_)
    166         throw new Error('Tab is not attached to this control.');
    167 
    168       tab.removeEventListener('selectedChange', this.boundOnTabSelectedChange_);
    169 
    170       if (this.selectedTab_ == tab) {
    171         if (this.tabs_.children.length) {
    172           this.tabs_.children[0].selected = true;
    173         } else {
    174           this.selectedTab_ = undefined;
    175         }
    176       }
    177 
    178       this.tabs_.removeChild(tab);
    179       tab.tabStripButton_.parentNode.removeChild(
    180           tab.tabStripButton_);
    181     },
    182 
    183     /**
    184      * Gets the currently selected tab element.
    185      */
    186     get selectedTab() {
    187       return this.selectedTab_;
    188     },
    189 
    190     /**
    191      * Sets the currently selected tab element.
    192      */
    193     set selectedTab(tab) {
    194       if (tab.parentNode != this.tabs_)
    195         throw Error('Tab is not part of this TabControl.');
    196       tab.selected = true;
    197     },
    198 
    199     /**
    200      * Hides the previously selected tab element and dispatches a
    201      * 'selectedTabChanged' event.
    202      */
    203     onTabSelectedChange_: function(e) {
    204       var tab = e.target;
    205       if (!e.newValue) {
    206         // Usually we can ignore this event, as the tab becoming unselected
    207         // needs no corrective action. However, if the currently selected
    208         // tab is deselected, we do need to do some work.
    209         if (tab == this.selectedTab_) {
    210           var previousTab = this.selectedTab_;
    211           var newTab;
    212           for (var i = 0; i < this.tabs_.children.length; ++i) {
    213             if (this.tabs_.children[i] != tab) {
    214               newTab = this.tabs_.children[i];
    215               break;
    216             }
    217           }
    218           if (newTab) {
    219             newTab.selected = true;
    220           } else {
    221             this.selectedTab_ = undefined;
    222             cr.dispatchPropertyChange(
    223                 this, 'selectedTab', this.selectedTab_, previousTab);
    224           }
    225         }
    226       } else {
    227         var previousTab = this.selectedTab_;
    228         this.selectedTab_ = tab;
    229         if (previousTab)
    230           previousTab.selected = false;
    231         cr.dispatchPropertyChange(
    232             this, 'selectedTab', this.selectedTab_, previousTab);
    233       }
    234     },
    235 
    236     /**
    237      * Returns an array of all the tabs within this control.  This is
    238      * not the same as this.children because the actual tab elements are
    239      * attached to the tabs_ element.
    240      */
    241     get tabs() {
    242       return Array.prototype.slice.call(this.tabs_.children);
    243     }
    244   };
    245 
    246   return {
    247     Tab: Tab,
    248     TabControl: TabControl
    249   };
    250 });
    251