expose combined context menu in tab header and add visual context menu button - fixes #6966

This commit is contained in:
Eugene Pankov 2022-09-05 00:24:21 +02:00
parent 3226a3d70f
commit fbea7db188
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
8 changed files with 89 additions and 45 deletions

View File

@ -13,13 +13,17 @@ profile-icon(
.name(
[title]='tab.customTitle || tab.title',
[class.no-hover]='config.store.terminal.hideCloseButton'
[class.no-hover]='config.store.terminal.hideCloseButton && config.store.terminal.hideTabOptionsButton'
cdkDrag,
cdkDragRootElement='tab-header',
[cdkDragData]='tab',
(cdkDragStarted)='onTabDragStart(tab)',
(cdkDragEnded)='onTabDragEnd()',
) {{tab.customTitle || tab.title}}
button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') ×
.buttons
button(*ngIf='!config.store.terminal.hideTabOptionsButton',(click)='onContextMenu($event)') !{require('../icons/tab-options.svg')}
button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') ×
ng-content

View File

@ -59,41 +59,49 @@ $tabs-height: 38px;
margin-left: 10px;
}
button {
display: block;
flex: none;
background: transparent;
opacity: 0;
-webkit-app-region: no-drag;
.buttons {
display: flex;
position: absolute;
right: 0;
align-items: center;
height: 100%;
top: 2px;
right: 3px;
opacity: 0;
$button-size: 26px;
width: $button-size;
height: $button-size;
border-radius: $button-size / 6;
line-height: $button-size;
align-self: center;
button {
display: flex;
align-items: center;
flex: none;
justify-content: center;
text-align: center;
font-size: 20px;
background: transparent;
-webkit-app-region: no-drag;
&:focus {
outline: 0;
$button-size: 26px;
width: $button-size;
height: $button-size;
border-radius: $button-size / 6;
line-height: $button-size;
align-self: center;
text-align: center;
font-size: 20px;
&:focus {
outline: 0;
}
}
}
&:hover .name:not(.no-hover) {
-webkit-mask-image: linear-gradient(black 0 0), linear-gradient(to left, transparent 0%, black 100%);
-webkit-mask-size: calc(100% - 60px) auto, 60px auto;
-webkit-mask-image: linear-gradient(black 0 0), linear-gradient(to left, transparent 0%, transparent 50%, black 100%);
-webkit-mask-size: calc(100% - 80px) auto, 80px auto;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: left, right;
}
&:hover button {
&:hover .buttons {
transition: 0.25s opacity;
display: block;
opacity: 1;
}

View File

@ -5,6 +5,7 @@ import { auditTime } from 'rxjs'
import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component'
import { SplitTabComponent } from './splitTab.component'
import { HotkeysService } from '../services/hotkeys.service'
import { AppService } from '../services/app.service'
import { HostAppService, Platform } from '../api/hostApp'
@ -69,10 +70,24 @@ export class TabHeaderComponent extends BaseComponent {
async buildContextMenu (): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = []
// Top-level tab menu
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this.tab, this)))) {
items.push({ type: 'separator' })
items = items.concat(section)
}
if (this.tab instanceof SplitTabComponent) {
const tab = this.tab.getFocusedTab()
if (tab) {
for (let section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(tab, this)))) {
// eslint-disable-next-line @typescript-eslint/no-loop-func
section = section.filter(item => !items.some(ex => ex.label === item.label))
if (section.length) {
items.push({ type: 'separator' })
items = items.concat(section)
}
}
}
}
return items.slice(1)
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M352 256C352 238.3 366.3 224 384 224C401.7 224 416 238.3 416 256C416 273.7 401.7 288 384 288C366.3 288 352 273.7 352 256zM192 256C192 238.3 206.3 224 224 224C241.7 224 256 238.3 256 256C256 273.7 241.7 288 224 288C206.3 288 192 273.7 192 256zM96 256C96 273.7 81.67 288 64 288C46.33 288 32 273.7 32 256C32 238.3 46.33 224 64 224C81.67 224 96 238.3 96 256z"/></svg>

After

Width:  |  Height:  |  Size: 623 B

View File

@ -41,7 +41,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
},
},
]
if (tabHeader) {
if (!tab.parent) {
items = [
...items,
{
@ -69,24 +69,22 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
},
},
]
} else {
if (tab.parent instanceof SplitTabComponent) {
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
items.push({
label: this.translate.instant('Split'),
submenu: directions.map(dir => ({
label: {
r: this.translate.instant('Right'),
b: this.translate.instant('Down'),
l: this.translate.instant('Left'),
t: this.translate.instant('Up'),
}[dir],
click: () => {
(tab.parent as SplitTabComponent).splitTab(tab, dir)
},
})) as MenuItemOptions[],
})
}
} else if (tab.parent instanceof SplitTabComponent) {
const directions: SplitDirection[] = ['r', 'b', 'l', 't']
items.push({
label: this.translate.instant('Split'),
submenu: directions.map(dir => ({
label: {
r: this.translate.instant('Right'),
b: this.translate.instant('Down'),
l: this.translate.instant('Left'),
t: this.translate.instant('Up'),
}[dir],
click: () => {
(tab.parent as SplitTabComponent).splitTab(tab, dir)
},
})) as MenuItemOptions[],
})
}
return items
}
@ -273,9 +271,9 @@ export class ProfilesContextMenu extends TabContextMenuItemProvider {
tab.destroy()
}
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
async getItems (tab: BaseTabComponent): Promise<MenuItemOptions[]> {
if (!tabHeader && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
if (tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) {
return [
{
label: this.translate.instant('Switch profile'),

View File

@ -295,6 +295,15 @@ h3.mt-4(translate) Tabs
(ngModelChange)='config.save();',
)
.form-line
.header
.title(translate) Hide tab options button
toggle(
[(ngModel)]='config.store.terminal.hideTabOptionsButton',
(ngModelChange)='config.save();',
)
.form-line
.header
.title(translate) Hide tab close button

View File

@ -22,6 +22,7 @@ export class TerminalConfigProvider extends ConfigProvider {
hideTabIndex: false,
showTabProfileIcon: false,
hideCloseButton: false,
hideTabOptionsButton: false,
rightClick: 'menu',
pasteOnMiddleClick: true,
copyOnSelect: false,

View File

@ -48,6 +48,14 @@ export class MiscContextMenu extends TabContextMenuItemProvider {
constructor (private translate: TranslateService) { super() }
async getItems (tab: BaseTabComponent): Promise<MenuItemOptions[]> {
if (tab instanceof BaseTerminalTabComponent && tab.enableToolbar && !tab.pinToolbar) {
return [{
label: this.translate.instant('Show toolbar'),
click: () => {
tab.pinToolbar = true
},
}]
}
if (tab instanceof BaseTerminalTabComponent && tab.session?.supportsWorkingDirectory()) {
return [{
label: this.translate.instant('Copy current path'),