mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-08 05:20:01 +00:00
macOS touchbar support
This commit is contained in:
parent
8f2d2cbe30
commit
5e07dd5442
@ -183,7 +183,6 @@ setupMenu = () => {
|
|||||||
{
|
{
|
||||||
role: 'window',
|
role: 'window',
|
||||||
submenu: [
|
submenu: [
|
||||||
{role: 'close'},
|
|
||||||
{role: 'minimize'},
|
{role: 'minimize'},
|
||||||
{role: 'zoom'},
|
{role: 'zoom'},
|
||||||
{type: 'separator'},
|
{type: 'separator'},
|
||||||
|
@ -11,6 +11,7 @@ import { DockingService } from '../services/docking.service'
|
|||||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
import { ThemesService } from '../services/themes.service'
|
import { ThemesService } from '../services/themes.service'
|
||||||
import { UpdaterService, Update } from '../services/updater.service'
|
import { UpdaterService, Update } from '../services/updater.service'
|
||||||
|
import { TouchbarService } from '../services/touchbar.service'
|
||||||
|
|
||||||
import { SafeModeModalComponent } from './safeModeModal.component'
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
@ -62,6 +63,7 @@ export class AppRootComponent {
|
|||||||
private tabRecovery: TabRecoveryService,
|
private tabRecovery: TabRecoveryService,
|
||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
private updater: UpdaterService,
|
private updater: UpdaterService,
|
||||||
|
private touchbar: TouchbarService,
|
||||||
public hostApp: HostAppService,
|
public hostApp: HostAppService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
@ -121,6 +123,8 @@ export class AppRootComponent {
|
|||||||
this.updater.check().then(update => {
|
this.updater.check().then(update => {
|
||||||
this.appUpdate = update
|
this.appUpdate = update
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.touchbar.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
onGlobalHotkey () {
|
onGlobalHotkey () {
|
||||||
|
@ -5,6 +5,7 @@ export abstract class BaseTabComponent {
|
|||||||
private static lastTabID = 0
|
private static lastTabID = 0
|
||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
|
titleChange$ = new Subject<string>()
|
||||||
customTitle: string
|
customTitle: string
|
||||||
scrollable: boolean
|
scrollable: boolean
|
||||||
hasActivity = false
|
hasActivity = false
|
||||||
@ -23,6 +24,13 @@ export abstract class BaseTabComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTitle (title: string) {
|
||||||
|
this.title = title]
|
||||||
|
if (!this.customTitle) {
|
||||||
|
this.titleChange$.next(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
displayActivity (): void {
|
displayActivity (): void {
|
||||||
this.hasActivity = true
|
this.hasActivity = true
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ export class TabHeaderComponent {
|
|||||||
let modal = this.ngbModal.open(RenameTabModalComponent)
|
let modal = this.ngbModal.open(RenameTabModalComponent)
|
||||||
modal.componentInstance.value = this.tab.customTitle || this.tab.title
|
modal.componentInstance.value = this.tab.customTitle || this.tab.title
|
||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
|
this.tab.setTitle(result)
|
||||||
this.tab.customTitle = result
|
this.tab.customTitle = result
|
||||||
}).catch(() => null)
|
}).catch(() => null)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
|
|||||||
import { DockingService } from './services/docking.service'
|
import { DockingService } from './services/docking.service'
|
||||||
import { TabRecoveryService } from './services/tabRecovery.service'
|
import { TabRecoveryService } from './services/tabRecovery.service'
|
||||||
import { ThemesService } from './services/themes.service'
|
import { ThemesService } from './services/themes.service'
|
||||||
|
import { TouchbarService } from './services/touchbar.service'
|
||||||
import { UpdaterService } from './services/updater.service'
|
import { UpdaterService } from './services/updater.service'
|
||||||
|
|
||||||
import { AppRootComponent } from './components/appRoot.component'
|
import { AppRootComponent } from './components/appRoot.component'
|
||||||
@ -44,6 +45,7 @@ const PROVIDERS = [
|
|||||||
LogService,
|
LogService,
|
||||||
TabRecoveryService,
|
TabRecoveryService,
|
||||||
ThemesService,
|
ThemesService,
|
||||||
|
TouchbarService,
|
||||||
UpdaterService,
|
UpdaterService,
|
||||||
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
||||||
{ provide: Theme, useClass: StandardTheme, multi: true },
|
{ provide: Theme, useClass: StandardTheme, multi: true },
|
||||||
|
@ -2,8 +2,8 @@ import { Subject, AsyncSubject } from 'rxjs'
|
|||||||
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
|
import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core'
|
||||||
import { DefaultTabProvider } from '../api/defaultTabProvider'
|
import { DefaultTabProvider } from '../api/defaultTabProvider'
|
||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { Logger, LogService } from '../services/log.service'
|
import { Logger, LogService } from './log.service'
|
||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from './config.service'
|
||||||
|
|
||||||
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
||||||
|
|
||||||
@ -11,9 +11,12 @@ export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
|||||||
export class AppService {
|
export class AppService {
|
||||||
tabs: BaseTabComponent[] = []
|
tabs: BaseTabComponent[] = []
|
||||||
activeTab: BaseTabComponent
|
activeTab: BaseTabComponent
|
||||||
|
activeTabChange$ = new Subject<BaseTabComponent>()
|
||||||
lastTabIndex = 0
|
lastTabIndex = 0
|
||||||
logger: Logger
|
logger: Logger
|
||||||
tabsChanged$ = new Subject<void>()
|
tabsChanged$ = new Subject<void>()
|
||||||
|
tabOpened$ = new Subject<BaseTabComponent>()
|
||||||
|
tabClosed$ = new Subject<BaseTabComponent>()
|
||||||
ready$ = new AsyncSubject<void>()
|
ready$ = new AsyncSubject<void>()
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@ -35,6 +38,7 @@ export class AppService {
|
|||||||
this.tabs.push(componentRef.instance)
|
this.tabs.push(componentRef.instance)
|
||||||
this.selectTab(componentRef.instance)
|
this.selectTab(componentRef.instance)
|
||||||
this.tabsChanged$.next()
|
this.tabsChanged$.next()
|
||||||
|
this.tabOpened$.next(componentRef.instance)
|
||||||
|
|
||||||
return componentRef.instance
|
return componentRef.instance
|
||||||
}
|
}
|
||||||
@ -59,6 +63,7 @@ export class AppService {
|
|||||||
this.activeTab.blurred$.next()
|
this.activeTab.blurred$.next()
|
||||||
}
|
}
|
||||||
this.activeTab = tab
|
this.activeTab = tab
|
||||||
|
this.activeTabChange$.next(tab)
|
||||||
if (this.activeTab) {
|
if (this.activeTab) {
|
||||||
this.activeTab.focused$.next()
|
this.activeTab.focused$.next()
|
||||||
}
|
}
|
||||||
@ -107,6 +112,7 @@ export class AppService {
|
|||||||
this.selectTab(this.tabs[newIndex])
|
this.selectTab(this.tabs[newIndex])
|
||||||
}
|
}
|
||||||
this.tabsChanged$.next()
|
this.tabsChanged$.next()
|
||||||
|
this.tabClosed$.next(tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
emitReady () {
|
emitReady () {
|
||||||
|
@ -10,6 +10,7 @@ export class ElectronService {
|
|||||||
globalShortcut: any
|
globalShortcut: any
|
||||||
screen: any
|
screen: any
|
||||||
remote: any
|
remote: any
|
||||||
|
TouchBar: typeof Electron.TouchBar
|
||||||
private electron: any
|
private electron: any
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@ -22,6 +23,7 @@ export class ElectronService {
|
|||||||
this.clipboard = this.electron.clipboard
|
this.clipboard = this.electron.clipboard
|
||||||
this.ipcRenderer = this.electron.ipcRenderer
|
this.ipcRenderer = this.electron.ipcRenderer
|
||||||
this.globalShortcut = this.remote.globalShortcut
|
this.globalShortcut = this.remote.globalShortcut
|
||||||
|
this.TouchBar = this.remote.TouchBar
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRequire (name: string): any {
|
remoteRequire (name: string): any {
|
||||||
|
69
terminus-core/src/services/touchbar.service.ts
Normal file
69
terminus-core/src/services/touchbar.service.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Injectable, Inject, NgZone } from '@angular/core'
|
||||||
|
import { Subject, Subscription } from 'rxjs'
|
||||||
|
import { AppService } from './app.service'
|
||||||
|
import { ConfigService } from './config.service'
|
||||||
|
import { ElectronService } from './electron.service'
|
||||||
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
|
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TouchbarService {
|
||||||
|
tabSelected$ = new Subject<number>()
|
||||||
|
private titleSubscriptions = new Map<BaseTabComponent, Subscription>()
|
||||||
|
private tabsSegmentedControl: Electron.TouchBarSegmentedControl
|
||||||
|
private tabSegments: Electron.SegmentedControlSegment[] = []
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private app: AppService,
|
||||||
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
|
private config: ConfigService,
|
||||||
|
private electron: ElectronService,
|
||||||
|
private zone: NgZone,
|
||||||
|
) {
|
||||||
|
app.tabsChanged$.subscribe(() => this.update())
|
||||||
|
app.activeTabChange$.subscribe(() => this.update())
|
||||||
|
app.tabOpened$.subscribe(tab => {
|
||||||
|
let sub = tab.titleChange$.subscribe(title => {
|
||||||
|
this.tabSegments[app.tabs.indexOf(tab)].label = title
|
||||||
|
this.tabsSegmentedControl.segments = this.tabSegments
|
||||||
|
})
|
||||||
|
this.titleSubscriptions.set(tab, sub)
|
||||||
|
})
|
||||||
|
app.tabClosed$.subscribe(tab => {
|
||||||
|
this.titleSubscriptions.get(tab).unsubscribe()
|
||||||
|
this.titleSubscriptions.delete(tab)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update () {
|
||||||
|
let buttons: IToolbarButton[] = []
|
||||||
|
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
||||||
|
buttons = buttons.concat(provider.provide())
|
||||||
|
})
|
||||||
|
buttons.sort((a, b) => (a.weight || 0) - (b.weight || 0))
|
||||||
|
this.tabSegments = this.app.tabs.map(tab => ({
|
||||||
|
label: tab.title,
|
||||||
|
}))
|
||||||
|
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
|
||||||
|
segments: this.tabSegments,
|
||||||
|
selectedIndex: this.app.tabs.indexOf(this.app.activeTab),
|
||||||
|
change: (selectedIndex) => this.zone.run(() => {
|
||||||
|
this.app.selectTab(this.app.tabs[selectedIndex])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let touchBar = new this.electron.TouchBar({
|
||||||
|
items: [
|
||||||
|
this.tabsSegmentedControl,
|
||||||
|
new this.electron.TouchBar.TouchBarSpacer({size: 'flexible'}),
|
||||||
|
new this.electron.TouchBar.TouchBarSpacer({size: 'small'}),
|
||||||
|
...buttons.map(button => new this.electron.TouchBar.TouchBarButton({
|
||||||
|
label: button.title,
|
||||||
|
// backgroundColor: '#0022cc',
|
||||||
|
click: () => this.zone.run(() => button.click()),
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
})
|
||||||
|
this.electron.app.window.setTouchBar(touchBar)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.hotkeyDescriptions = config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
this.hotkeyDescriptions = config.enabledServices(hotkeyProviders).map(x => x.hotkeys).reduce((a, b) => a.concat(b))
|
||||||
this.title = 'Settings'
|
this.setTitle('Settings')
|
||||||
this.scrollable = true
|
this.scrollable = true
|
||||||
this.screens = this.docking.getScreens()
|
this.screens = this.docking.getScreens()
|
||||||
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
||||||
|
@ -18,17 +18,14 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activate () {
|
activate () {
|
||||||
let modal = this.ngbModal.open(SSHModalComponent)
|
this.ngbModal.open(SSHModalComponent)
|
||||||
modal.result.then(() => {
|
|
||||||
//this.terminal.openTab(shell)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): IToolbarButton[] {
|
||||||
return [{
|
return [{
|
||||||
icon: 'globe',
|
icon: 'globe',
|
||||||
weight: 5,
|
weight: 5,
|
||||||
title: 'SSH connections',
|
title: 'SSH',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.activate()
|
this.activate()
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.decorators = this.decorators || []
|
this.decorators = this.decorators || []
|
||||||
this.title = 'Terminal'
|
this.setTitle('Terminal')
|
||||||
this.resize$.first().subscribe(async (resizeEvent) => {
|
this.resize$.first().subscribe(async (resizeEvent) => {
|
||||||
if (!this.session) {
|
if (!this.session) {
|
||||||
this.session = this.sessions.addSession(
|
this.session = this.sessions.addSession(
|
||||||
@ -211,11 +211,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachHTermHandlers (hterm: any) {
|
attachHTermHandlers (hterm: any) {
|
||||||
hterm.setWindowTitle = (title) => {
|
hterm.setWindowTitle = title => this.zone.run(() => this.setTitle(title))
|
||||||
this.zone.run(() => {
|
|
||||||
this.title = title
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const _setAlternateMode = hterm.setAlternateMode.bind(hterm)
|
const _setAlternateMode = hterm.setAlternateMode.bind(hterm)
|
||||||
hterm.setAlternateMode = (state) => {
|
hterm.setAlternateMode = (state) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user