TabBar.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. module INSPECTOR {
  2. /**
  3. * A tab bar will contains each view the inspector can have : Canvas2D, Meshes...
  4. * The default active tab is the first one of the list.
  5. */
  6. export class TabBar extends BasicElement {
  7. // The list of available tabs
  8. private _tabs: Array<Tab> = [];
  9. private _inspector: Inspector;
  10. /** The tab displaying all meshes */
  11. private _meshTab: MeshTab;
  12. /** The toolbar */
  13. private _toolBar: Toolbar;
  14. /** The icon displayed at the end of the toolbar displaying a combo box of tabs not displayed */
  15. private _moreTabsIcon: HTMLElement;
  16. /** The panel displayed when the 'more-tab' icon is selected */
  17. private _moreTabsPanel: HTMLElement;
  18. /** The list of tab displayed by clicking on the remainingIcon */
  19. private _invisibleTabs: Array<Tab> = [];
  20. /** The list of tabs visible, displayed in the tab bar */
  21. private _visibleTabs: Array<Tab> = [];
  22. constructor(inspector: Inspector, initialTab?: number) {
  23. super();
  24. this._inspector = inspector;
  25. this._tabs.push(new SceneTab(this, this._inspector));
  26. this._tabs.push(new ConsoleTab(this, this._inspector));
  27. this._tabs.push(new StatsTab(this, this._inspector));
  28. this._meshTab = new MeshTab(this, this._inspector);
  29. this._tabs.push(new TextureTab(this, this._inspector));
  30. this._tabs.push(this._meshTab);
  31. this._tabs.push(new LightTab(this, this._inspector));
  32. this._tabs.push(new MaterialTab(this, this._inspector));
  33. if (GLTFTab.IsSupported) {
  34. this._tabs.push(new GLTFTab(this, this._inspector));
  35. }
  36. if (BABYLON.GUI) {
  37. this._tabs.push(new GUITab(this, this._inspector));
  38. }
  39. this._tabs.push(new PhysicsTab(this, this._inspector));
  40. this._tabs.push(new CameraTab(this, this._inspector));
  41. this._tabs.push(new SoundTab(this, this._inspector));
  42. this._tabs.push(new ToolsTab(this, this._inspector));
  43. this._toolBar = new Toolbar(this._inspector);
  44. this._build();
  45. //Check initialTab is defined and between tabs bounds
  46. if (!initialTab || initialTab < 0 || initialTab >= this._tabs.length) {
  47. initialTab = 0;
  48. }
  49. this._tabs[initialTab].active(true);
  50. // set all tab as visible
  51. for (let tab of this._tabs) {
  52. this._visibleTabs.push(tab);
  53. }
  54. }
  55. // No update
  56. public update() { }
  57. protected _build() {
  58. this._div.className = 'tabbar';
  59. this._div.appendChild(this._toolBar.toHtml());
  60. for (let tab of this._tabs) {
  61. this._div.appendChild(tab.toHtml());
  62. }
  63. this._moreTabsIcon = Helpers.CreateElement('i', 'fa fa-angle-double-right more-tabs');
  64. this._moreTabsPanel = Helpers.CreateDiv('more-tabs-panel');
  65. this._moreTabsIcon.addEventListener('click', () => {
  66. // Hide the 'more-tabs-panel' if already displayed
  67. if (this._moreTabsPanel.style.display == 'flex') {
  68. this._moreTabsPanel.style.display = 'none';
  69. } else {
  70. // Attach more-tabs-panel if not attached yet
  71. let topPanel = this._div.parentNode as HTMLElement;
  72. if (!topPanel.contains(this._moreTabsPanel)) {
  73. topPanel.appendChild(this._moreTabsPanel);
  74. }
  75. // Clean the 'more-tabs-panel'
  76. Helpers.CleanDiv(this._moreTabsPanel);
  77. // Add each invisible tabs to this panel
  78. for (let tab of this._invisibleTabs) {
  79. this._addInvisibleTabToPanel(tab);
  80. }
  81. // And display it
  82. this._moreTabsPanel.style.display = 'flex';
  83. }
  84. });
  85. }
  86. /**
  87. * Add a tab to the 'more-tabs' panel, displayed by clicking on the
  88. * 'more-tabs' icon
  89. */
  90. private _addInvisibleTabToPanel(tab: Tab) {
  91. let div = Helpers.CreateDiv('invisible-tab', this._moreTabsPanel);
  92. div.textContent = tab.name;
  93. div.addEventListener('click', () => {
  94. this._moreTabsPanel.style.display = 'none';
  95. this.switchTab(tab);
  96. });
  97. }
  98. /** Dispose the current tab, set the given tab as active, and refresh the treeview */
  99. public switchTab(tab: Tab) {
  100. // Dispose the active tab
  101. let activeTab = this.getActiveTab();
  102. if (activeTab) {
  103. activeTab.dispose();
  104. }
  105. // Deactivate all tabs
  106. for (let t of this._tabs) {
  107. t.active(false);
  108. }
  109. // activate the given tab
  110. tab.active(true);
  111. // Refresh the inspector
  112. this._inspector.refresh();
  113. }
  114. /** Display the mesh tab.
  115. * If a parameter is given, the given mesh details are displayed
  116. */
  117. public switchMeshTab(mesh?: BABYLON.AbstractMesh) {
  118. this.switchTab(this._meshTab);
  119. if (mesh) {
  120. let item = this._meshTab.getItemFor(mesh);
  121. if (item) {
  122. this._meshTab.select(item);
  123. }
  124. }
  125. }
  126. /** Returns the active tab */
  127. public getActiveTab(): BABYLON.Nullable<Tab> {
  128. for (let tab of this._tabs) {
  129. if (tab.isActive()) {
  130. return tab;
  131. }
  132. }
  133. return null;
  134. }
  135. public getActiveTabIndex(): number {
  136. for (let i = 0; i < this._tabs.length; i++) {
  137. if (this._tabs[i].isActive()) {
  138. return i;
  139. }
  140. }
  141. return 0;
  142. }
  143. public get inspector(): Inspector {
  144. return this._inspector;
  145. }
  146. /**
  147. * Returns the total width in pixel of the tabbar,
  148. * that corresponds to the sum of the width of each visible tab + toolbar width
  149. */
  150. public getPixelWidth(): number {
  151. let sum = 0;
  152. for (let tab of this._visibleTabs) {
  153. sum += tab.getPixelWidth();
  154. }
  155. sum += this._toolBar.getPixelWidth();
  156. if (this._div.contains(this._moreTabsIcon)) {
  157. sum += 30; // $tabbarheight
  158. }
  159. return sum;
  160. }
  161. /** Display the remaining icon or not depending on the tabbar width.
  162. * This function should be called each time the inspector width is updated
  163. */
  164. public updateWidth(): void {
  165. if (!this._div.parentElement) {
  166. return;
  167. }
  168. let parentSize = this._div.parentElement.clientWidth;
  169. let lastTabWidth = 75;
  170. let currentSize = this.getPixelWidth();
  171. // Check if a tab should be removed : if the tab bar width is greater than
  172. // its parent width
  173. while (this._visibleTabs.length > 0 && currentSize > parentSize) {
  174. // Start by the last element
  175. let tab = this._visibleTabs.pop();
  176. if (!tab) {
  177. break;
  178. }
  179. // set it invisible
  180. this._invisibleTabs.push(tab);
  181. // and removes it from the DOM
  182. this._div.removeChild(tab.toHtml());
  183. currentSize = this.getPixelWidth() + lastTabWidth;
  184. }
  185. // Check if a tab can be added to the tab bar : if the tab bar width
  186. // + 100 (at least 100px is needed to add a tab) is less than its parent width
  187. if (this._invisibleTabs.length > 0) {
  188. if (currentSize + lastTabWidth < parentSize) {
  189. let lastTab = this._invisibleTabs.pop();
  190. if (lastTab) {
  191. this._div.appendChild(lastTab.toHtml());
  192. this._visibleTabs.push(lastTab);
  193. }
  194. // Update more-tab icon in last position if needed
  195. if (this._div.contains(this._moreTabsIcon)) {
  196. this._div.removeChild(this._moreTabsIcon);
  197. }
  198. }
  199. }
  200. if (this._invisibleTabs.length > 0 && !this._div.contains(this._moreTabsIcon)) {
  201. this._div.appendChild(this._moreTabsIcon);
  202. }
  203. }
  204. }
  205. }