mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-24 13:29:55 +00:00
unify context menus (fixes #2054)
This commit is contained in:
parent
286b6cfdde
commit
f4f52321f5
@ -1,4 +1,5 @@
|
||||
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component'
|
||||
export { TabHeaderComponent } from '../components/tabHeader.component'
|
||||
export { SplitTabComponent, SplitContainer } from '../components/splitTab.component'
|
||||
export { TabRecoveryProvider, RecoveredTab } from './tabRecovery'
|
||||
export { ToolbarButtonProvider, ToolbarButton } from './toolbarButtonProvider'
|
||||
|
@ -17,39 +17,49 @@ export class CloseContextMenu extends TabContextMenuItemProvider {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
return [
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
let items = [
|
||||
{
|
||||
label: 'Close',
|
||||
click: () => this.zone.run(() => {
|
||||
this.app.closeTab(tab, true)
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Close other tabs',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.filter(x => x !== tab)) {
|
||||
this.app.closeTab(t, true)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the right',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
|
||||
this.app.closeTab(t, true)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the left',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
|
||||
this.app.closeTab(t, true)
|
||||
if (this.app.tabs.includes(tab)) {
|
||||
this.app.closeTab(tab, true)
|
||||
} else {
|
||||
tab.destroy()
|
||||
}
|
||||
}),
|
||||
},
|
||||
]
|
||||
if (tabHeader) {
|
||||
items = [
|
||||
...items,
|
||||
{
|
||||
label: 'Close other tabs',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.filter(x => x !== tab)) {
|
||||
this.app.closeTab(t, true)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the right',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.slice(this.app.tabs.indexOf(tab) + 1)) {
|
||||
this.app.closeTab(t, true)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Close tabs to the left',
|
||||
click: () => this.zone.run(() => {
|
||||
for (const t of this.app.tabs.slice(0, this.app.tabs.indexOf(tab))) {
|
||||
this.app.closeTab(t, true)
|
||||
}
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,28 +86,31 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
return [
|
||||
{
|
||||
label: 'Rename',
|
||||
click: () => this.zone.run(() => tabHeader?.showRenameTabModal()),
|
||||
},
|
||||
{
|
||||
label: 'Duplicate',
|
||||
click: () => this.zone.run(() => this.app.duplicateTab(tab)),
|
||||
},
|
||||
{
|
||||
label: 'Color',
|
||||
sublabel: COLORS.find(x => x.value === tab.color)!.name,
|
||||
submenu: COLORS.map(color => ({
|
||||
label: color.name,
|
||||
type: 'radio',
|
||||
checked: tab.color === color.value,
|
||||
click: () => this.zone.run(() => {
|
||||
tab.color = color.value
|
||||
}),
|
||||
})) as Electron.MenuItemConstructorOptions[],
|
||||
},
|
||||
]
|
||||
if (tabHeader) {
|
||||
return [
|
||||
{
|
||||
label: 'Rename',
|
||||
click: () => this.zone.run(() => tabHeader?.showRenameTabModal()),
|
||||
},
|
||||
{
|
||||
label: 'Duplicate',
|
||||
click: () => this.zone.run(() => this.app.duplicateTab(tab)),
|
||||
},
|
||||
{
|
||||
label: 'Color',
|
||||
sublabel: COLORS.find(x => x.value === tab.color)!.name,
|
||||
submenu: COLORS.map(color => ({
|
||||
label: color.name,
|
||||
type: 'radio',
|
||||
checked: tab.color === color.value,
|
||||
click: () => this.zone.run(() => {
|
||||
tab.color = color.value
|
||||
}),
|
||||
})) as Electron.MenuItemConstructorOptions[],
|
||||
},
|
||||
]
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { first } from 'rxjs/operators'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { NgZone, OnInit, OnDestroy, Inject, Injector, Optional, ViewChild, HostBinding, Input, ElementRef } from '@angular/core'
|
||||
import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger } from 'terminus-core'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, Platform, LogService, Logger, TabContextMenuItemProvider } from 'terminus-core'
|
||||
|
||||
import { BaseSession, SessionsService } from '../services/sessions.service'
|
||||
import { TerminalFrontendService } from '../services/terminalFrontend.service'
|
||||
@ -11,7 +11,6 @@ import { TerminalFrontendService } from '../services/terminalFrontend.service'
|
||||
import { Frontend } from '../frontends/frontend'
|
||||
import { ResizeEvent } from './interfaces'
|
||||
import { TerminalDecorator } from './decorator'
|
||||
import { TerminalContextMenuItemProvider } from './contextMenuProvider'
|
||||
|
||||
|
||||
/** @hidden */
|
||||
@ -88,7 +87,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
@Inject(ToastrService) protected toastr: ToastrServiceProxy,
|
||||
protected log: LogService,
|
||||
@Optional() @Inject(TerminalDecorator) protected decorators: TerminalDecorator[],
|
||||
@Optional() @Inject(TerminalContextMenuItemProvider) protected contextMenuProviders: TerminalContextMenuItemProvider[],
|
||||
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
|
||||
) {
|
||||
super()
|
||||
this.logger = log.create('baseTerminalTab')
|
||||
|
@ -2,6 +2,7 @@ import { BaseTerminalTabComponent } from './baseTerminalTab.component'
|
||||
|
||||
/**
|
||||
* Extend to add more terminal context menu items
|
||||
* @deprecated
|
||||
*/
|
||||
export abstract class TerminalContextMenuItemProvider {
|
||||
weight: number
|
||||
|
@ -1,98 +0,0 @@
|
||||
import { NgZone, Injectable } from '@angular/core'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService } from 'terminus-core'
|
||||
import { UACService } from './services/uac.service'
|
||||
import { TerminalService } from './services/terminal.service'
|
||||
import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
|
||||
import { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class NewTabContextMenu extends TerminalContextMenuItemProvider {
|
||||
weight = 0
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private zone: NgZone,
|
||||
private terminalService: TerminalService,
|
||||
private uac: UACService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTerminalTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
const profiles = await this.terminalService.getProfiles()
|
||||
|
||||
const items: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'New terminal',
|
||||
click: () => this.zone.run(() => {
|
||||
this.terminalService.openTabWithOptions((tab as any).sessionOptions)
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'New with profile',
|
||||
submenu: profiles.map(profile => ({
|
||||
label: profile.name,
|
||||
click: () => this.zone.run(async () => {
|
||||
const workingDirectory = this.config.store.terminal.alwaysUseWorkingDirectory === true ?
|
||||
this.config.store.terminal.workingDirectory : await tab.session.getWorkingDirectory()
|
||||
await this.terminalService.openTab(profile, workingDirectory)
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
|
||||
if (this.uac.isAvailable) {
|
||||
items.push({
|
||||
label: 'New admin tab',
|
||||
submenu: profiles.map(profile => ({
|
||||
label: profile.name,
|
||||
click: () => this.zone.run(async () => {
|
||||
this.terminalService.openTabWithOptions({
|
||||
...profile.sessionOptions,
|
||||
runAsAdministrator: true,
|
||||
})
|
||||
}),
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class CopyPasteContextMenu extends TerminalContextMenuItemProvider {
|
||||
weight = 1
|
||||
|
||||
constructor (
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTerminalTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
return [
|
||||
{
|
||||
label: 'Copy',
|
||||
click: () => {
|
||||
this.zone.run(() => {
|
||||
setTimeout(() => {
|
||||
tab.frontend.copySelection()
|
||||
this.toastr.info('Copied')
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Paste',
|
||||
click: () => {
|
||||
this.zone.run(() => tab.paste())
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
@ -35,8 +35,7 @@ import { PathDropDecorator } from './pathDrop'
|
||||
import { TerminalConfigProvider } from './config'
|
||||
import { TerminalHotkeyProvider } from './hotkeys'
|
||||
import { HyperColorSchemes } from './colorSchemes'
|
||||
import { NewTabContextMenu, CopyPasteContextMenu } from './contextMenu'
|
||||
import { SaveAsProfileContextMenu } from './tabContextMenu'
|
||||
import { NewTabContextMenu, CopyPasteContextMenu, SaveAsProfileContextMenu, LegacyContextMenu } from './tabContextMenu'
|
||||
import { ZModemDecorator } from './zmodem'
|
||||
|
||||
import { CmderShellProvider } from './shells/cmder'
|
||||
@ -92,10 +91,10 @@ import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
|
||||
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
|
||||
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
|
||||
|
||||
{ provide: TerminalContextMenuItemProvider, useClass: NewTabContextMenu, multi: true },
|
||||
{ provide: TerminalContextMenuItemProvider, useClass: CopyPasteContextMenu, multi: true },
|
||||
|
||||
{ provide: TabContextMenuItemProvider, useClass: NewTabContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: CopyPasteContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: SaveAsProfileContextMenu, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: LegacyContextMenu, multi: true },
|
||||
|
||||
// For WindowsDefaultShellProvider
|
||||
PowerShellCoreShellProvider,
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Injectable, NgZone } from '@angular/core'
|
||||
import { Injectable, NgZone, Optional, Inject } from '@angular/core'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider } from 'terminus-core'
|
||||
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent } from 'terminus-core'
|
||||
import { TerminalTabComponent } from './components/terminalTab.component'
|
||||
import { UACService } from './services/uac.service'
|
||||
import { TerminalService } from './services/terminal.service'
|
||||
import { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
|
||||
import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
@ -12,13 +14,11 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
|
||||
private config: ConfigService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
private uac: UACService,
|
||||
private terminalService: TerminalService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
if (!(tab instanceof TerminalTabComponent)) {
|
||||
return []
|
||||
}
|
||||
@ -43,18 +43,140 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
|
||||
},
|
||||
]
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class NewTabContextMenu extends TabContextMenuItemProvider {
|
||||
weight = 10
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private zone: NgZone,
|
||||
private terminalService: TerminalService,
|
||||
private uac: UACService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
const profiles = await this.terminalService.getProfiles()
|
||||
|
||||
const items: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'New terminal',
|
||||
click: () => this.zone.run(() => {
|
||||
this.terminalService.openTabWithOptions((tab as any).sessionOptions)
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'New with profile',
|
||||
submenu: profiles.map(profile => ({
|
||||
label: profile.name,
|
||||
click: () => this.zone.run(async () => {
|
||||
let workingDirectory = this.config.store.terminal.workingDirectory
|
||||
if (this.config.store.terminal.alwaysUseWorkingDirectory !== true && tab instanceof TerminalTabComponent) {
|
||||
workingDirectory = await tab.session.getWorkingDirectory()
|
||||
}
|
||||
await this.terminalService.openTab(profile, workingDirectory)
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
|
||||
if (this.uac.isAvailable) {
|
||||
items.push({
|
||||
label: 'New admin tab',
|
||||
submenu: profiles.map(profile => ({
|
||||
label: profile.name,
|
||||
click: () => this.zone.run(async () => {
|
||||
this.terminalService.openTabWithOptions({
|
||||
...profile.sessionOptions,
|
||||
runAsAdministrator: true,
|
||||
})
|
||||
}),
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) {
|
||||
items.push({
|
||||
label: 'Duplicate as administrator',
|
||||
click: () => this.zone.run(async () => {
|
||||
this.terminalService.openTabWithOptions({
|
||||
...tab.sessionOptions,
|
||||
...(tab as TerminalTabComponent).sessionOptions,
|
||||
runAsAdministrator: true,
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class CopyPasteContextMenu extends TabContextMenuItemProvider {
|
||||
weight = 1
|
||||
|
||||
constructor (
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
if (tabHeader) {
|
||||
return []
|
||||
}
|
||||
if (tab instanceof BaseTerminalTabComponent) {
|
||||
return [
|
||||
{
|
||||
label: 'Copy',
|
||||
click: () => {
|
||||
this.zone.run(() => {
|
||||
setTimeout(() => {
|
||||
(tab as BaseTerminalTabComponent).frontend.copySelection()
|
||||
this.toastr.info('Copied')
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Paste',
|
||||
click: () => {
|
||||
this.zone.run(() => (tab as BaseTerminalTabComponent).paste())
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class LegacyContextMenu extends TabContextMenuItemProvider {
|
||||
weight = 1
|
||||
|
||||
constructor (
|
||||
@Optional() @Inject(TerminalContextMenuItemProvider) protected contextMenuProviders: TerminalContextMenuItemProvider[],
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
if (tab instanceof BaseTerminalTabComponent) {
|
||||
let items: Electron.MenuItemConstructorOptions[] = []
|
||||
for (const p of this.contextMenuProviders) {
|
||||
items = items.concat(await p.getItems(tab))
|
||||
}
|
||||
return items
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user