1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import m from 'mithril'; 16import {TraceImpl} from '../../core/trace_impl'; 17import {Button} from '../../widgets/button'; 18import {MenuItem, PopupMenu} from '../../widgets/menu'; 19import {Tab, TabbedSplitPanel} from '../../widgets/tabbed_split_panel'; 20import {DEFAULT_DETAILS_CONTENT_HEIGHT} from '../css_constants'; 21import {CurrentSelectionTab} from './current_selection_tab'; 22 23export interface TabPanelAttrs { 24 readonly trace: TraceImpl; 25 readonly className?: string; 26} 27 28export class TabPanel implements m.ClassComponent<TabPanelAttrs> { 29 view({ 30 attrs, 31 children, 32 }: m.Vnode<TabPanelAttrs, this>): m.Children | null | void { 33 const tabs = this.gatherTabs(attrs.trace); 34 35 return m( 36 TabbedSplitPanel, 37 { 38 className: attrs.className, 39 startingHeight: DEFAULT_DETAILS_CONTENT_HEIGHT, 40 leftHandleContent: this.renderDropdownMenu(attrs.trace), 41 tabs, 42 visibility: attrs.trace.tabs.tabPanelVisibility, 43 onVisibilityChange: (visibility) => 44 attrs.trace.tabs.setTabPanelVisibility(visibility), 45 onTabChange: (key) => attrs.trace.tabs.showTab(key), 46 currentTabKey: attrs.trace.tabs.currentTabUri, 47 }, 48 children, 49 ); 50 } 51 52 private gatherTabs(trace: TraceImpl) { 53 const tabMan = trace.tabs; 54 const tabList = trace.tabs.openTabsUri; 55 const resolvedTabs = tabMan.resolveTabs(tabList); 56 57 const tabs = resolvedTabs.map(({uri, tab: tabDesc}): Tab => { 58 if (tabDesc) { 59 return { 60 key: uri, 61 hasCloseButton: true, 62 title: tabDesc.content.getTitle(), 63 content: tabDesc.content.render(), 64 onClose: () => { 65 trace.tabs.hideTab(uri); 66 }, 67 }; 68 } else { 69 return { 70 key: uri, 71 hasCloseButton: true, 72 title: 'Tab does not exist', 73 content: undefined, 74 }; 75 } 76 }); 77 78 // Add the permanent current selection tab to the front of the list of tabs 79 tabs.unshift({ 80 key: 'current_selection', 81 title: 'Current Selection', 82 content: m(CurrentSelectionTab, {trace}), 83 }); 84 85 return tabs; 86 } 87 88 private renderDropdownMenu(trace: TraceImpl): m.Child { 89 const entries = trace.tabs.tabs 90 .filter((tab) => tab.isEphemeral === false) 91 .map(({content, uri}) => { 92 return { 93 key: uri, 94 title: content.getTitle(), 95 onClick: () => trace.tabs.toggleTab(uri), 96 checked: trace.tabs.isOpen(uri), 97 }; 98 }); 99 100 return m( 101 PopupMenu, 102 { 103 trigger: m(Button, { 104 compact: true, 105 icon: 'more_vert', 106 disabled: entries.length === 0, 107 title: 'More Tabs', 108 }), 109 }, 110 entries.map((entry) => { 111 return m(MenuItem, { 112 key: entry.key, 113 label: entry.title, 114 onclick: () => entry.onClick(), 115 icon: entry.checked ? 'check_box' : 'check_box_outline_blank', 116 }); 117 }), 118 ); 119 } 120} 121