Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 36 super(true); | 36 super(true); |
| 37 this.registerRequiredCSS('ui/tabbedPane.css'); | 37 this.registerRequiredCSS('ui/tabbedPane.css'); |
| 38 this.element.classList.add('tabbed-pane'); | 38 this.element.classList.add('tabbed-pane'); |
| 39 this.contentElement.classList.add('tabbed-pane-shadow'); | 39 this.contentElement.classList.add('tabbed-pane-shadow'); |
| 40 this.contentElement.tabIndex = -1; | 40 this.contentElement.tabIndex = -1; |
| 41 this._headerElement = this.contentElement.createChild('div', 'tabbed-pane-he ader'); | 41 this._headerElement = this.contentElement.createChild('div', 'tabbed-pane-he ader'); |
| 42 this._headerContentsElement = this._headerElement.createChild('div', 'tabbed -pane-header-contents'); | 42 this._headerContentsElement = this._headerElement.createChild('div', 'tabbed -pane-header-contents'); |
| 43 this._tabSlider = createElementWithClass('div', 'tabbed-pane-tab-slider'); | 43 this._tabSlider = createElementWithClass('div', 'tabbed-pane-tab-slider'); |
| 44 this._tabsElement = this._headerContentsElement.createChild('div', 'tabbed-p ane-header-tabs'); | 44 this._tabsElement = this._headerContentsElement.createChild('div', 'tabbed-p ane-header-tabs'); |
| 45 this._tabsElement.setAttribute('role', 'tablist'); | 45 this._tabsElement.setAttribute('role', 'tablist'); |
| 46 this._tabsElement.addEventListener('keydown', this._keyDown.bind(this), fals e); | |
| 46 this._contentElement = this.contentElement.createChild('div', 'tabbed-pane-c ontent'); | 47 this._contentElement = this.contentElement.createChild('div', 'tabbed-pane-c ontent'); |
| 47 this._contentElement.setAttribute('role', 'tabpanel'); | 48 this._contentElement.setAttribute('role', 'tabpanel'); |
| 48 this._contentElement.createChild('content'); | 49 this._contentElement.createChild('content'); |
| 49 /** @type {!Array.<!UI.TabbedPaneTab>} */ | 50 /** @type {!Array.<!UI.TabbedPaneTab>} */ |
| 50 this._tabs = []; | 51 this._tabs = []; |
| 51 /** @type {!Array.<!UI.TabbedPaneTab>} */ | 52 /** @type {!Array.<!UI.TabbedPaneTab>} */ |
| 52 this._tabsHistory = []; | 53 this._tabsHistory = []; |
| 53 /** @type {!Map<string, !UI.TabbedPaneTab>} */ | 54 /** @type {!Map<string, !UI.TabbedPaneTab>} */ |
| 54 this._tabsById = new Map(); | 55 this._tabsById = new Map(); |
| 55 this._currentTabLocked = false; | 56 this._currentTabLocked = false; |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 } | 211 } |
| 211 | 212 |
| 212 /** | 213 /** |
| 213 * @param {string} id | 214 * @param {string} id |
| 214 * @param {boolean=} userGesture | 215 * @param {boolean=} userGesture |
| 215 */ | 216 */ |
| 216 closeTab(id, userGesture) { | 217 closeTab(id, userGesture) { |
| 217 this.closeTabs([id], userGesture); | 218 this.closeTabs([id], userGesture); |
| 218 } | 219 } |
| 219 | 220 |
| 221 | |
| 220 /** | 222 /** |
| 221 * @param {!Array.<string>} ids | 223 * @param {!Array.<string>} ids |
| 222 * @param {boolean=} userGesture | 224 * @param {boolean=} userGesture |
| 223 */ | 225 */ |
| 224 closeTabs(ids, userGesture) { | 226 closeTabs(ids, userGesture) { |
| 225 var focused = this.hasFocus(); | 227 var focused = this.hasFocus(); |
| 226 for (var i = 0; i < ids.length; ++i) | 228 for (var i = 0; i < ids.length; ++i) |
| 227 this._innerCloseTab(ids[i], userGesture); | 229 this._innerCloseTab(ids[i], userGesture); |
| 228 this._updateTabElements(); | 230 this._updateTabElements(); |
| 229 if (this._tabsHistory.length) | 231 if (this._tabsHistory.length) |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 break; | 301 break; |
| 300 } | 302 } |
| 301 } | 303 } |
| 302 if (index === -1) | 304 if (index === -1) |
| 303 return []; | 305 return []; |
| 304 return this._tabs.slice(index + 1).map(function(tab) { | 306 return this._tabs.slice(index + 1).map(function(tab) { |
| 305 return tab.id; | 307 return tab.id; |
| 306 }); | 308 }); |
| 307 } | 309 } |
| 308 | 310 |
| 311 _viewHasFocus() { | |
| 312 if (this.visibleView) | |
| 313 return this.visibleView.hasFocus(); | |
| 314 return this.contentElement === this.contentElement.getComponentRoot().active Element; | |
| 315 } | |
| 316 | |
| 309 /** | 317 /** |
| 310 * @param {string} id | 318 * @param {string} id |
| 311 * @param {boolean=} userGesture | 319 * @param {boolean=} userGesture |
| 312 * @return {boolean} | 320 * @return {boolean} |
| 313 */ | 321 */ |
| 314 selectTab(id, userGesture) { | 322 selectTab(id, userGesture) { |
| 315 if (this._currentTabLocked) | 323 if (this._currentTabLocked) |
| 316 return false; | 324 return false; |
| 317 var focused = this.hasFocus(); | 325 var focused = this._viewHasFocus(); |
| 318 var tab = this._tabsById.get(id); | 326 var tab = this._tabsById.get(id); |
| 319 if (!tab) | 327 if (!tab) |
| 320 return false; | 328 return false; |
| 321 if (this._currentTab && this._currentTab.id === id) | 329 if (this._currentTab && this._currentTab.id === id) |
| 322 return true; | 330 return true; |
| 323 | 331 |
| 324 this.suspendInvalidations(); | 332 this.suspendInvalidations(); |
| 325 this._hideCurrentTab(); | 333 this._hideCurrentTab(); |
| 326 this._showTab(tab); | 334 this._showTab(tab); |
| 327 this.resumeInvalidations(); | 335 this.resumeInvalidations(); |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 536 */ | 544 */ |
| 537 _hideTabElement(tab) { | 545 _hideTabElement(tab) { |
| 538 this._tabsElement.removeChild(tab.tabElement); | 546 this._tabsElement.removeChild(tab.tabElement); |
| 539 tab._shown = false; | 547 tab._shown = false; |
| 540 } | 548 } |
| 541 | 549 |
| 542 _createDropDownButton() { | 550 _createDropDownButton() { |
| 543 var dropDownContainer = createElementWithClass('div', 'tabbed-pane-header-ta bs-drop-down-container'); | 551 var dropDownContainer = createElementWithClass('div', 'tabbed-pane-header-ta bs-drop-down-container'); |
| 544 var chevronIcon = UI.Icon.create('largeicon-chevron', 'chevron-icon'); | 552 var chevronIcon = UI.Icon.create('largeicon-chevron', 'chevron-icon'); |
| 545 dropDownContainer.appendChild(chevronIcon); | 553 dropDownContainer.appendChild(chevronIcon); |
| 554 dropDownContainer.addEventListener('click', this._onDropDownMouseDown.bind(t his)); | |
| 546 dropDownContainer.addEventListener('mousedown', this._onDropDownMouseDown.bi nd(this)); | 555 dropDownContainer.addEventListener('mousedown', this._onDropDownMouseDown.bi nd(this)); |
| 547 return dropDownContainer; | 556 return dropDownContainer; |
| 548 } | 557 } |
| 549 | 558 |
| 550 /** | 559 /** |
| 551 * @param {!Event} event | 560 * @param {!Event} event |
| 552 */ | 561 */ |
| 553 _onDropDownMouseDown(event) { | 562 _onDropDownMouseDown(event) { |
| 554 if (event.which !== 1) | 563 if (event.which !== 1) |
| 555 return; | 564 return; |
| 556 var menu = new UI.ContextMenu(event); | 565 var rect = this._dropDownButton.getBoundingClientRect(); |
| 566 var menu = new UI.ContextMenu(event, false, rect.left, rect.bottom); | |
| 557 for (var i = 0; i < this._tabs.length; ++i) { | 567 for (var i = 0; i < this._tabs.length; ++i) { |
| 558 var tab = this._tabs[i]; | 568 var tab = this._tabs[i]; |
| 559 if (tab._shown) | 569 if (tab._shown) |
| 560 continue; | 570 continue; |
| 561 menu.appendCheckboxItem(tab.title, this._dropDownMenuItemSelected.bind(thi s, tab), this._tabsHistory[0] === tab); | 571 menu.appendCheckboxItem(tab.title, this._dropDownMenuItemSelected.bind(thi s, tab), this._tabsHistory[0] === tab); |
| 562 } | 572 } |
| 563 menu.show(); | 573 menu.show(); |
| 564 } | 574 } |
| 565 | 575 |
| 566 /** | 576 /** |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 749 return; | 759 return; |
| 750 | 760 |
| 751 this._hideTab(this._currentTab); | 761 this._hideTab(this._currentTab); |
| 752 delete this._currentTab; | 762 delete this._currentTab; |
| 753 } | 763 } |
| 754 | 764 |
| 755 /** | 765 /** |
| 756 * @param {!UI.TabbedPaneTab} tab | 766 * @param {!UI.TabbedPaneTab} tab |
| 757 */ | 767 */ |
| 758 _showTab(tab) { | 768 _showTab(tab) { |
| 769 tab.tabElement.tabIndex = 0; | |
| 759 tab.tabElement.classList.add('selected'); | 770 tab.tabElement.classList.add('selected'); |
| 760 UI.ARIAUtils.setSelected(tab.tabElement, true); | 771 UI.ARIAUtils.setSelected(tab.tabElement, true); |
| 761 tab.view.show(this.element); | 772 tab.view.show(this.element); |
| 762 this._updateTabSlider(); | 773 this._updateTabSlider(); |
| 763 } | 774 } |
| 764 | 775 |
| 765 _updateTabSlider() { | 776 _updateTabSlider() { |
| 766 if (!this._currentTab || !this._sliderEnabled) | 777 if (!this._currentTab || !this._sliderEnabled) |
| 767 return; | 778 return; |
| 768 var left = 0; | 779 var left = 0; |
| 769 for (var i = 0; i < this._tabs.length && this._currentTab !== this._tabs[i] && this._tabs[i]._shown; i++) | 780 for (var i = 0; i < this._tabs.length && this._currentTab !== this._tabs[i] && this._tabs[i]._shown; i++) |
| 770 left += this._tabs[i]._measuredWidth; | 781 left += this._tabs[i]._measuredWidth; |
| 771 var sliderWidth = this._currentTab._shown ? this._currentTab._measuredWidth : this._dropDownButton.offsetWidth; | 782 var sliderWidth = this._currentTab._shown ? this._currentTab._measuredWidth : this._dropDownButton.offsetWidth; |
| 772 var scaleFactor = window.devicePixelRatio >= 1.5 ? ' scaleY(0.75)' : ''; | 783 var scaleFactor = window.devicePixelRatio >= 1.5 ? ' scaleY(0.75)' : ''; |
| 773 this._tabSlider.style.transform = 'translateX(' + left + 'px)' + scaleFactor ; | 784 this._tabSlider.style.transform = 'translateX(' + left + 'px)' + scaleFactor ; |
| 774 this._tabSlider.style.width = sliderWidth + 'px'; | 785 this._tabSlider.style.width = sliderWidth + 'px'; |
| 775 | 786 |
| 776 if (this._tabSlider.parentElement !== this._headerContentsElement) | 787 if (this._tabSlider.parentElement !== this._headerContentsElement) |
| 777 this._headerContentsElement.appendChild(this._tabSlider); | 788 this._headerContentsElement.appendChild(this._tabSlider); |
| 778 } | 789 } |
| 779 | 790 |
| 780 /** | 791 /** |
| 781 * @param {!UI.TabbedPaneTab} tab | 792 * @param {!UI.TabbedPaneTab} tab |
| 782 */ | 793 */ |
| 783 _hideTab(tab) { | 794 _hideTab(tab) { |
| 795 tab.tabElement.removeAttribute('tabIndex'); | |
| 784 tab.tabElement.classList.remove('selected'); | 796 tab.tabElement.classList.remove('selected'); |
| 785 tab.tabElement.setAttribute('aria-selected', 'false'); | 797 tab.tabElement.setAttribute('aria-selected', 'false'); |
| 786 tab.view.detach(); | 798 tab.view.detach(); |
| 787 } | 799 } |
| 788 | 800 |
| 789 /** | 801 /** |
| 790 * @override | 802 * @override |
| 791 * @return {!Array.<!Element>} | 803 * @return {!Array.<!Element>} |
| 792 */ | 804 */ |
| 793 elementsToRestoreScrollPositionsFor() { | 805 elementsToRestoreScrollPositionsFor() { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 835 } | 847 } |
| 836 | 848 |
| 837 /** | 849 /** |
| 838 * @param {boolean} allow | 850 * @param {boolean} allow |
| 839 * @param {boolean=} automatic | 851 * @param {boolean=} automatic |
| 840 */ | 852 */ |
| 841 setAllowTabReorder(allow, automatic) { | 853 setAllowTabReorder(allow, automatic) { |
| 842 this._allowTabReorder = allow; | 854 this._allowTabReorder = allow; |
| 843 this._automaticReorder = automatic; | 855 this._automaticReorder = automatic; |
| 844 } | 856 } |
| 857 | |
| 858 /** | |
| 859 * @param {!Event} event | |
| 860 */ | |
| 861 _keyDown(event) { | |
| 862 if (!this._currentTab) | |
| 863 return; | |
| 864 var nextTabElement = null; | |
| 865 switch (event.keyCode) { | |
| 866 case UI.KeyboardShortcut.Keys.Up.code: | |
| 867 case UI.KeyboardShortcut.Keys.Left.code: | |
| 868 nextTabElement = this._currentTab.tabElement.previousElementSibling; | |
|
aboxhall
2017/04/17 23:03:36
Sorry for not noticing this the first time around,
einbinder
2017/04/18 18:45:22
It would be nicer to use this._tabs, but this._tab
| |
| 869 if (!nextTabElement && !this._dropDownButton.parentElement) | |
| 870 nextTabElement = this._currentTab.tabElement.parentElement.lastElement Child; | |
| 871 break; | |
| 872 case UI.KeyboardShortcut.Keys.Down.code: | |
| 873 case UI.KeyboardShortcut.Keys.Right.code: | |
| 874 nextTabElement = this._currentTab.tabElement.nextElementSibling; | |
| 875 if (!nextTabElement && !this._dropDownButton.parentElement) | |
| 876 nextTabElement = this._currentTab.tabElement.parentElement.firstElemen tChild; | |
| 877 break; | |
| 878 case UI.KeyboardShortcut.Keys.Enter.code: | |
| 879 case UI.KeyboardShortcut.Keys.Space.code: | |
| 880 this._currentTab.view.focus(); | |
| 881 return; | |
| 882 default: | |
| 883 return; | |
| 884 } | |
| 885 if (!nextTabElement) { | |
| 886 this._dropDownButton.click(); | |
| 887 return; | |
| 888 } | |
| 889 var tab = this._tabs.find(tab => tab.tabElement === nextTabElement); | |
| 890 this.selectTab(tab.id, true); | |
| 891 nextTabElement.focus(); | |
|
aboxhall
2017/04/17 23:03:36
Why not make focusing the tab part of selectTab()?
einbinder
2017/04/18 18:45:21
selectTab is also called for things like Ctrl+] sh
| |
| 892 } | |
| 845 }; | 893 }; |
| 846 | 894 |
| 847 /** @enum {symbol} */ | 895 /** @enum {symbol} */ |
| 848 UI.TabbedPane.Events = { | 896 UI.TabbedPane.Events = { |
| 849 TabSelected: Symbol('TabSelected'), | 897 TabSelected: Symbol('TabSelected'), |
| 850 TabClosed: Symbol('TabClosed'), | 898 TabClosed: Symbol('TabClosed'), |
| 851 TabOrderChanged: Symbol('TabOrderChanged') | 899 TabOrderChanged: Symbol('TabOrderChanged') |
| 852 }; | 900 }; |
| 853 | 901 |
| 854 /** | 902 /** |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1019 tabElement.__iconElement = iconContainer; | 1067 tabElement.__iconElement = iconContainer; |
| 1020 } | 1068 } |
| 1021 | 1069 |
| 1022 /** | 1070 /** |
| 1023 * @param {boolean} measuring | 1071 * @param {boolean} measuring |
| 1024 * @return {!Element} | 1072 * @return {!Element} |
| 1025 */ | 1073 */ |
| 1026 _createTabElement(measuring) { | 1074 _createTabElement(measuring) { |
| 1027 var tabElement = createElementWithClass('div', 'tabbed-pane-header-tab'); | 1075 var tabElement = createElementWithClass('div', 'tabbed-pane-header-tab'); |
| 1028 tabElement.id = 'tab-' + this._id; | 1076 tabElement.id = 'tab-' + this._id; |
| 1029 tabElement.tabIndex = -1; | |
| 1030 UI.ARIAUtils.markAsTab(tabElement); | 1077 UI.ARIAUtils.markAsTab(tabElement); |
| 1031 UI.ARIAUtils.setSelected(tabElement, false); | 1078 UI.ARIAUtils.setSelected(tabElement, false); |
| 1032 tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPa ne, this.id, true); | 1079 tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPa ne, this.id, true); |
| 1033 | 1080 |
| 1034 var titleElement = tabElement.createChild('span', 'tabbed-pane-header-tab-ti tle'); | 1081 var titleElement = tabElement.createChild('span', 'tabbed-pane-header-tab-ti tle'); |
| 1035 titleElement.textContent = this.title; | 1082 titleElement.textContent = this.title; |
| 1036 titleElement.title = this.tooltip || ''; | 1083 titleElement.title = this.tooltip || ''; |
| 1037 this._createIconElement(tabElement, titleElement, measuring); | 1084 this._createIconElement(tabElement, titleElement, measuring); |
| 1038 if (!measuring) | 1085 if (!measuring) |
| 1039 this._titleElement = titleElement; | 1086 this._titleElement = titleElement; |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1220 * @param {!Array.<string>} ids | 1267 * @param {!Array.<string>} ids |
| 1221 */ | 1268 */ |
| 1222 closeTabs(tabbedPane, ids) {}, | 1269 closeTabs(tabbedPane, ids) {}, |
| 1223 | 1270 |
| 1224 /** | 1271 /** |
| 1225 * @param {string} tabId | 1272 * @param {string} tabId |
| 1226 * @param {!UI.ContextMenu} contextMenu | 1273 * @param {!UI.ContextMenu} contextMenu |
| 1227 */ | 1274 */ |
| 1228 onContextMenu(tabId, contextMenu) {} | 1275 onContextMenu(tabId, contextMenu) {} |
| 1229 }; | 1276 }; |
| OLD | NEW |