ViewManager.js 13 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. export default class ViewManager{constructor(){this._views=new Map();this._locationNameByViewId=new Map();for(const extension of self.runtime.extensions('view')){const descriptor=extension.descriptor();this._views.set(descriptor['id'],new UI.ProvidedView(extension));this._locationNameByViewId.set(descriptor['id'],descriptor['location']);}}
  2. static _createToolbar(toolbarItems){if(!toolbarItems.length){return null;}
  3. const toolbar=new UI.Toolbar('');for(const item of toolbarItems){toolbar.appendToolbarItem(item);}
  4. return toolbar.element;}
  5. revealView(view){const location=(view[_Location.symbol]);if(!location){return Promise.resolve();}
  6. location._reveal();return location.showView(view);}
  7. view(viewId){return this._views.get(viewId);}
  8. materializedWidget(viewId){const view=this.view(viewId);return view?view[UI.View.widgetSymbol]:null;}
  9. showView(viewId,userGesture,omitFocus){const view=this._views.get(viewId);if(!view){console.error('Could not find view for id: \''+viewId+'\' '+new Error().stack);return Promise.resolve();}
  10. const locationName=this._locationNameByViewId.get(viewId);const location=view[_Location.symbol];if(location){location._reveal();return location.showView(view,undefined,userGesture,omitFocus);}
  11. return this.resolveLocation(locationName).then(location=>{if(!location){throw new Error('Could not resolve location for view: '+viewId);}
  12. location._reveal();return location.showView(view,undefined,userGesture,omitFocus);});}
  13. resolveLocation(location){if(!location){return(Promise.resolve(null));}
  14. const resolverExtensions=self.runtime.extensions(UI.ViewLocationResolver).filter(extension=>extension.descriptor()['name']===location);if(!resolverExtensions.length){throw new Error('Unresolved location: '+location);}
  15. const resolverExtension=resolverExtensions[0];return resolverExtension.instance().then(resolver=>(resolver.resolveLocation(location)));}
  16. createTabbedLocation(revealCallback,location,restoreSelection,allowReorder,defaultTab){return new UI.ViewManager._TabbedLocation(this,revealCallback,location,restoreSelection,allowReorder,defaultTab);}
  17. createStackLocation(revealCallback,location){return new _StackLocation(this,revealCallback,location);}
  18. hasViewsForLocation(location){return!!this._viewsForLocation(location).length;}
  19. _viewsForLocation(location){const result=[];for(const id of this._views.keys()){if(this._locationNameByViewId.get(id)===location){result.push(this._views.get(id));}}
  20. return result;}}
  21. export class _ContainerWidget extends UI.VBox{constructor(view){super();this.element.classList.add('flex-auto','view-container','overflow-auto');this._view=view;this.element.tabIndex=-1;UI.ARIAUtils.markAsTabpanel(this.element);UI.ARIAUtils.setAccessibleName(this.element,ls`${view.title()} panel`);this.setDefaultFocusedElement(this.element);}
  22. _materialize(){if(this._materializePromise){return this._materializePromise;}
  23. const promises=[];promises.push(this._view.toolbarItems().then(toolbarItems=>{const toolbarElement=UI.ViewManager._createToolbar(toolbarItems);if(toolbarElement){this.element.insertBefore(toolbarElement,this.element.firstChild);}}));promises.push(this._view.widget().then(widget=>{const shouldFocus=this.element.hasFocus();this.setDefaultFocusedElement(null);this._view[UI.View.widgetSymbol]=widget;widget.show(this.element);if(shouldFocus){widget.focus();}}));this._materializePromise=Promise.all(promises);return this._materializePromise;}
  24. wasShown(){this._materialize().then(()=>{this._wasShownForTest();});}
  25. _wasShownForTest(){}}
  26. export class _ExpandableContainerWidget extends UI.VBox{constructor(view){super(true);this.element.classList.add('flex-none');this.registerRequiredCSS('ui/viewContainers.css');this._titleElement=createElementWithClass('div','expandable-view-title');UI.ARIAUtils.markAsButton(this._titleElement);this._titleExpandIcon=UI.Icon.create('smallicon-triangle-right','title-expand-icon');this._titleElement.appendChild(this._titleExpandIcon);const titleText=view.title();this._titleElement.createTextChild(titleText);UI.ARIAUtils.setAccessibleName(this._titleElement,titleText);this._titleElement.tabIndex=0;self.onInvokeElement(this._titleElement,this._toggleExpanded.bind(this));this._titleElement.addEventListener('keydown',this._onTitleKeyDown.bind(this),false);this.contentElement.insertBefore(this._titleElement,this.contentElement.firstChild);UI.ARIAUtils.setControls(this._titleElement,this.contentElement.createChild('slot'));this._view=view;view[UI.ViewManager._ExpandableContainerWidget._symbol]=this;}
  27. _materialize(){if(this._materializePromise){return this._materializePromise;}
  28. const promises=[];promises.push(this._view.toolbarItems().then(toolbarItems=>{const toolbarElement=UI.ViewManager._createToolbar(toolbarItems);if(toolbarElement){this._titleElement.appendChild(toolbarElement);}}));promises.push(this._view.widget().then(widget=>{this._widget=widget;this._view[UI.View.widgetSymbol]=widget;widget.show(this.element);}));this._materializePromise=Promise.all(promises);return this._materializePromise;}
  29. _expand(){if(this._titleElement.classList.contains('expanded')){return this._materialize();}
  30. this._titleElement.classList.add('expanded');UI.ARIAUtils.setExpanded(this._titleElement,true);this._titleExpandIcon.setIconType('smallicon-triangle-down');return this._materialize().then(()=>this._widget.show(this.element));}
  31. _collapse(){if(!this._titleElement.classList.contains('expanded')){return;}
  32. this._titleElement.classList.remove('expanded');UI.ARIAUtils.setExpanded(this._titleElement,false);this._titleExpandIcon.setIconType('smallicon-triangle-right');this._materialize().then(()=>this._widget.detach());}
  33. _toggleExpanded(event){if(event.type==='keydown'&&event.target!==this._titleElement){return;}
  34. if(this._titleElement.classList.contains('expanded')){this._collapse();}else{this._expand();}}
  35. _onTitleKeyDown(event){if(event.target!==this._titleElement){return;}
  36. if(event.key==='ArrowLeft'){this._collapse();}else if(event.key==='ArrowRight'){if(!this._titleElement.classList.contains('expanded')){this._expand();}else if(this._widget){this._widget.focus();}}}}
  37. _ExpandableContainerWidget._symbol=Symbol('container');class _Location{constructor(manager,widget,revealCallback){this._manager=manager;this._revealCallback=revealCallback;this._widget=widget;}
  38. widget(){return this._widget;}
  39. _reveal(){if(this._revealCallback){this._revealCallback();}}}
  40. _Location.symbol=Symbol('location');export class _TabbedLocation extends _Location{constructor(manager,revealCallback,location,restoreSelection,allowReorder,defaultTab){const tabbedPane=new UI.TabbedPane();if(allowReorder){tabbedPane.setAllowTabReorder(true);}
  41. super(manager,tabbedPane,revealCallback);this._tabbedPane=tabbedPane;this._allowReorder=allowReorder;this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected,this._tabSelected,this);this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabClosed,this._tabClosed,this);this._closeableTabSetting=Common.settings.createSetting(location+'-closeableTabs',{});this._tabOrderSetting=Common.settings.createSetting(location+'-tabOrder',{});this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabOrderChanged,this._persistTabOrder,this);if(restoreSelection){this._lastSelectedTabSetting=Common.settings.createSetting(location+'-selectedTab','');}
  42. this._defaultTab=defaultTab;this._views=new Map();if(location){this.appendApplicableItems(location);}}
  43. widget(){return this._tabbedPane;}
  44. tabbedPane(){return this._tabbedPane;}
  45. enableMoreTabsButton(){const moreTabsButton=new UI.ToolbarMenuButton(this._appendTabsToMenu.bind(this));this._tabbedPane.leftToolbar().appendToolbarItem(moreTabsButton);this._tabbedPane.disableOverflowMenu();return moreTabsButton;}
  46. appendApplicableItems(locationName){const views=this._manager._viewsForLocation(locationName);if(this._allowReorder){let i=0;const persistedOrders=this._tabOrderSetting.get();const orders=new Map();for(const view of views){orders.set(view.viewId(),persistedOrders[view.viewId()]||(++i)*UI.ViewManager._TabbedLocation.orderStep);}
  47. views.sort((a,b)=>orders.get(a.viewId())-orders.get(b.viewId()));}
  48. for(const view of views){const id=view.viewId();this._views.set(id,view);view[_Location.symbol]=this;if(view.isTransient()){continue;}
  49. if(!view.isCloseable()){this._appendTab(view);}else if(this._closeableTabSetting.get()[id]){this._appendTab(view);}}
  50. if(this._defaultTab&&this._tabbedPane.hasTab(this._defaultTab)){this._tabbedPane.selectTab(this._defaultTab);}else if(this._lastSelectedTabSetting&&this._tabbedPane.hasTab(this._lastSelectedTabSetting.get())){this._tabbedPane.selectTab(this._lastSelectedTabSetting.get());}}
  51. _appendTabsToMenu(contextMenu){const views=Array.from(this._views.values());views.sort((viewa,viewb)=>viewa.title().localeCompare(viewb.title()));for(const view of views){const title=Common.UIString(view.title());contextMenu.defaultSection().appendItem(title,this.showView.bind(this,view,undefined,true));}}
  52. _appendTab(view,index){this._tabbedPane.appendTab(view.viewId(),view.title(),new UI.ViewManager._ContainerWidget(view),undefined,false,view.isCloseable()||view.isTransient(),index);}
  53. appendView(view,insertBefore){if(this._tabbedPane.hasTab(view.viewId())){return;}
  54. const oldLocation=view[_Location.symbol];if(oldLocation&&oldLocation!==this){oldLocation.removeView(view);}
  55. view[_Location.symbol]=this;this._manager._views.set(view.viewId(),view);this._views.set(view.viewId(),view);let index=undefined;const tabIds=this._tabbedPane.tabIds();if(this._allowReorder){const orderSetting=this._tabOrderSetting.get();const order=orderSetting[view.viewId()];for(let i=0;order&&i<tabIds.length;++i){if(orderSetting[tabIds[i]]&&orderSetting[tabIds[i]]>order){index=i;break;}}}else if(insertBefore){for(let i=0;i<tabIds.length;++i){if(tabIds[i]===insertBefore.viewId()){index=i;break;}}}
  56. this._appendTab(view,index);if(view.isCloseable()){const tabs=this._closeableTabSetting.get();const tabId=view.viewId();if(!tabs[tabId]){tabs[tabId]=true;this._closeableTabSetting.set(tabs);}}
  57. this._persistTabOrder();}
  58. showView(view,insertBefore,userGesture,omitFocus){this.appendView(view,insertBefore);this._tabbedPane.selectTab(view.viewId(),userGesture);if(!omitFocus){this._tabbedPane.focus();}
  59. const widget=(this._tabbedPane.tabView(view.viewId()));return widget._materialize();}
  60. removeView(view){if(!this._tabbedPane.hasTab(view.viewId())){return;}
  61. delete view[_Location.symbol];this._manager._views.delete(view.viewId());this._tabbedPane.closeTab(view.viewId());this._views.delete(view.viewId());}
  62. _tabSelected(event){const tabId=(event.data.tabId);if(this._lastSelectedTabSetting&&event.data['isUserGesture']){this._lastSelectedTabSetting.set(tabId);}}
  63. _tabClosed(event){const id=(event.data['tabId']);const tabs=this._closeableTabSetting.get();if(tabs[id]){delete tabs[id];this._closeableTabSetting.set(tabs);}
  64. this._views.get(id).disposeView();}
  65. _persistTabOrder(){const tabIds=this._tabbedPane.tabIds();const tabOrders={};for(let i=0;i<tabIds.length;i++){tabOrders[tabIds[i]]=(i+1)*UI.ViewManager._TabbedLocation.orderStep;}
  66. const oldTabOrder=this._tabOrderSetting.get();const oldTabArray=Object.keys(oldTabOrder);oldTabArray.sort((a,b)=>oldTabOrder[a]-oldTabOrder[b]);let lastOrder=0;for(const key of oldTabArray){if(key in tabOrders){lastOrder=tabOrders[key];continue;}
  67. tabOrders[key]=++lastOrder;}
  68. this._tabOrderSetting.set(tabOrders);}}
  69. _TabbedLocation.orderStep=10;class _StackLocation extends _Location{constructor(manager,revealCallback,location){const vbox=new UI.VBox();super(manager,vbox,revealCallback);this._vbox=vbox;this._expandableContainers=new Map();if(location){this.appendApplicableItems(location);}}
  70. appendView(view,insertBefore){const oldLocation=view[_Location.symbol];if(oldLocation&&oldLocation!==this){oldLocation.removeView(view);}
  71. let container=this._expandableContainers.get(view.viewId());if(!container){view[_Location.symbol]=this;this._manager._views.set(view.viewId(),view);container=new UI.ViewManager._ExpandableContainerWidget(view);let beforeElement=null;if(insertBefore){const beforeContainer=insertBefore[UI.ViewManager._ExpandableContainerWidget._symbol];beforeElement=beforeContainer?beforeContainer.element:null;}
  72. container.show(this._vbox.contentElement,beforeElement);this._expandableContainers.set(view.viewId(),container);}}
  73. showView(view,insertBefore){this.appendView(view,insertBefore);const container=this._expandableContainers.get(view.viewId());return container._expand();}
  74. removeView(view){const container=this._expandableContainers.get(view.viewId());if(!container){return;}
  75. container.detach();this._expandableContainers.delete(view.viewId());delete view[_Location.symbol];this._manager._views.delete(view.viewId());}
  76. appendApplicableItems(locationName){for(const view of this._manager._viewsForLocation(locationName)){this.appendView(view);}}}
  77. self.UI=self.UI||{};UI=UI||{};UI.viewManager;UI.ViewManager=ViewManager;UI.ViewManager._ContainerWidget=_ContainerWidget;UI.ViewManager._ExpandableContainerWidget=_ExpandableContainerWidget;UI.ViewManager._TabbedLocation=_TabbedLocation;