diff --git a/app/main.js b/app/main.js index 38693abc..9d56da49 100644 --- a/app/main.js +++ b/app/main.js @@ -183,7 +183,6 @@ setupMenu = () => { { role: 'window', submenu: [ - {role: 'close'}, {role: 'minimize'}, {role: 'zoom'}, {type: 'separator'}, diff --git a/terminus-core/src/components/appRoot.component.ts b/terminus-core/src/components/appRoot.component.ts index ab3f15fc..ce1ee98a 100644 --- a/terminus-core/src/components/appRoot.component.ts +++ b/terminus-core/src/components/appRoot.component.ts @@ -11,6 +11,7 @@ import { DockingService } from '../services/docking.service' import { TabRecoveryService } from '../services/tabRecovery.service' import { ThemesService } from '../services/themes.service' import { UpdaterService, Update } from '../services/updater.service' +import { TouchbarService } from '../services/touchbar.service' import { SafeModeModalComponent } from './safeModeModal.component' import { AppService, IToolbarButton, ToolbarButtonProvider } from '../api' @@ -62,6 +63,7 @@ export class AppRootComponent { private tabRecovery: TabRecoveryService, private hotkeys: HotkeysService, private updater: UpdaterService, + private touchbar: TouchbarService, public hostApp: HostAppService, public config: ConfigService, public app: AppService, @@ -121,6 +123,8 @@ export class AppRootComponent { this.updater.check().then(update => { this.appUpdate = update }) + + this.touchbar.update() } onGlobalHotkey () { diff --git a/terminus-core/src/components/baseTab.component.ts b/terminus-core/src/components/baseTab.component.ts index 77a15538..60a9a664 100644 --- a/terminus-core/src/components/baseTab.component.ts +++ b/terminus-core/src/components/baseTab.component.ts @@ -5,6 +5,7 @@ export abstract class BaseTabComponent { private static lastTabID = 0 id: number title: string + titleChange$ = new Subject() customTitle: string scrollable: boolean 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 { this.hasActivity = true } diff --git a/terminus-core/src/components/tabHeader.component.ts b/terminus-core/src/components/tabHeader.component.ts index 348933f6..be54e722 100644 --- a/terminus-core/src/components/tabHeader.component.ts +++ b/terminus-core/src/components/tabHeader.component.ts @@ -69,6 +69,7 @@ export class TabHeaderComponent { let modal = this.ngbModal.open(RenameTabModalComponent) modal.componentInstance.value = this.tab.customTitle || this.tab.title modal.result.then(result => { + this.tab.setTitle(result) this.tab.customTitle = result }).catch(() => null) } diff --git a/terminus-core/src/index.ts b/terminus-core/src/index.ts index af9ee658..0c6e7234 100644 --- a/terminus-core/src/index.ts +++ b/terminus-core/src/index.ts @@ -14,6 +14,7 @@ import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service' import { DockingService } from './services/docking.service' import { TabRecoveryService } from './services/tabRecovery.service' import { ThemesService } from './services/themes.service' +import { TouchbarService } from './services/touchbar.service' import { UpdaterService } from './services/updater.service' import { AppRootComponent } from './components/appRoot.component' @@ -44,6 +45,7 @@ const PROVIDERS = [ LogService, TabRecoveryService, ThemesService, + TouchbarService, UpdaterService, { provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true }, { provide: Theme, useClass: StandardTheme, multi: true }, diff --git a/terminus-core/src/services/app.service.ts b/terminus-core/src/services/app.service.ts index 51f82be5..2f4e647d 100644 --- a/terminus-core/src/services/app.service.ts +++ b/terminus-core/src/services/app.service.ts @@ -2,8 +2,8 @@ import { Subject, AsyncSubject } from 'rxjs' import { Injectable, ComponentFactoryResolver, Injector, Optional } from '@angular/core' import { DefaultTabProvider } from '../api/defaultTabProvider' import { BaseTabComponent } from '../components/baseTab.component' -import { Logger, LogService } from '../services/log.service' -import { ConfigService } from '../services/config.service' +import { Logger, LogService } from './log.service' +import { ConfigService } from './config.service' export declare type TabComponentType = new (...args: any[]) => BaseTabComponent @@ -11,9 +11,12 @@ export declare type TabComponentType = new (...args: any[]) => BaseTabComponent export class AppService { tabs: BaseTabComponent[] = [] activeTab: BaseTabComponent + activeTabChange$ = new Subject() lastTabIndex = 0 logger: Logger tabsChanged$ = new Subject() + tabOpened$ = new Subject() + tabClosed$ = new Subject() ready$ = new AsyncSubject() constructor ( @@ -35,6 +38,7 @@ export class AppService { this.tabs.push(componentRef.instance) this.selectTab(componentRef.instance) this.tabsChanged$.next() + this.tabOpened$.next(componentRef.instance) return componentRef.instance } @@ -59,6 +63,7 @@ export class AppService { this.activeTab.blurred$.next() } this.activeTab = tab + this.activeTabChange$.next(tab) if (this.activeTab) { this.activeTab.focused$.next() } @@ -107,6 +112,7 @@ export class AppService { this.selectTab(this.tabs[newIndex]) } this.tabsChanged$.next() + this.tabClosed$.next(tab) } emitReady () { diff --git a/terminus-core/src/services/electron.service.ts b/terminus-core/src/services/electron.service.ts index 07124a0d..d76e9df8 100644 --- a/terminus-core/src/services/electron.service.ts +++ b/terminus-core/src/services/electron.service.ts @@ -10,6 +10,7 @@ export class ElectronService { globalShortcut: any screen: any remote: any + TouchBar: typeof Electron.TouchBar private electron: any constructor () { @@ -22,6 +23,7 @@ export class ElectronService { this.clipboard = this.electron.clipboard this.ipcRenderer = this.electron.ipcRenderer this.globalShortcut = this.remote.globalShortcut + this.TouchBar = this.remote.TouchBar } remoteRequire (name: string): any { diff --git a/terminus-core/src/services/touchbar.service.ts b/terminus-core/src/services/touchbar.service.ts new file mode 100644 index 00000000..def333fa --- /dev/null +++ b/terminus-core/src/services/touchbar.service.ts @@ -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() + private titleSubscriptions = new Map() + 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) + } + +} diff --git a/terminus-settings/src/components/settingsTab.component.ts b/terminus-settings/src/components/settingsTab.component.ts index b62671d1..dbe860eb 100644 --- a/terminus-settings/src/components/settingsTab.component.ts +++ b/terminus-settings/src/components/settingsTab.component.ts @@ -28,7 +28,7 @@ export class SettingsTabComponent extends BaseTabComponent { ) { super() 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.screens = this.docking.getScreens() this.settingsProviders = config.enabledServices(this.settingsProviders) diff --git a/terminus-ssh/src/buttonProvider.ts b/terminus-ssh/src/buttonProvider.ts index a4a88a07..f94279ea 100644 --- a/terminus-ssh/src/buttonProvider.ts +++ b/terminus-ssh/src/buttonProvider.ts @@ -18,17 +18,14 @@ export class ButtonProvider extends ToolbarButtonProvider { } activate () { - let modal = this.ngbModal.open(SSHModalComponent) - modal.result.then(() => { - //this.terminal.openTab(shell) - }) + this.ngbModal.open(SSHModalComponent) } provide (): IToolbarButton[] { return [{ icon: 'globe', weight: 5, - title: 'SSH connections', + title: 'SSH', click: async () => { this.activate() } diff --git a/terminus-terminal/src/components/terminalTab.component.ts b/terminus-terminal/src/components/terminalTab.component.ts index adc6633d..459ef2c6 100644 --- a/terminus-terminal/src/components/terminalTab.component.ts +++ b/terminus-terminal/src/components/terminalTab.component.ts @@ -58,7 +58,7 @@ export class TerminalTabComponent extends BaseTabComponent { ) { super() this.decorators = this.decorators || [] - this.title = 'Terminal' + this.setTitle('Terminal') this.resize$.first().subscribe(async (resizeEvent) => { if (!this.session) { this.session = this.sessions.addSession( @@ -211,11 +211,7 @@ export class TerminalTabComponent extends BaseTabComponent { } attachHTermHandlers (hterm: any) { - hterm.setWindowTitle = (title) => { - this.zone.run(() => { - this.title = title - }) - } + hterm.setWindowTitle = title => this.zone.run(() => this.setTitle(title)) const _setAlternateMode = hterm.setAlternateMode.bind(hterm) hterm.setAlternateMode = (state) => {