From 482343e38339c35ccf92028f37ac24b10b7c529d Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Sun, 26 Mar 2017 22:39:10 +0200 Subject: [PATCH] . --- app/src/api/tab.ts | 7 +- app/src/services/config.ts | 13 ++-- app/src/services/plugins.ts | 41 +++-------- app/src/settings/api.ts | 11 +++ app/src/settings/components/settingsPane.less | 12 +--- app/src/settings/components/settingsPane.pug | 72 ++----------------- app/src/settings/components/settingsPane.ts | 46 +----------- .../settings/components/settingsTabBody.ts | 25 +++++++ app/src/settings/index.ts | 2 + app/src/terminal/components/settings.pug | 56 +++++++++++++++ app/src/terminal/components/settings.scss | 9 +++ app/src/terminal/components/settings.ts | 42 +++++++++++ app/src/terminal/index.ts | 9 ++- app/src/terminal/settings.ts | 13 ++++ 14 files changed, 197 insertions(+), 161 deletions(-) create mode 100644 app/src/settings/api.ts create mode 100644 app/src/settings/components/settingsTabBody.ts create mode 100644 app/src/terminal/components/settings.pug create mode 100644 app/src/terminal/components/settings.scss create mode 100644 app/src/terminal/components/settings.ts create mode 100644 app/src/terminal/settings.ts diff --git a/app/src/api/tab.ts b/app/src/api/tab.ts index 7957d8ee..02a47570 100644 --- a/app/src/api/tab.ts +++ b/app/src/api/tab.ts @@ -3,7 +3,8 @@ import { BaseTabComponent } from 'components/baseTab' export declare type ComponentType = new (...args: any[]) => BaseTabComponent -export class Tab { + +export abstract class Tab { id: number title: string scrollable: boolean @@ -20,9 +21,7 @@ export class Tab { this.hasActivity = true } - getComponentType (): ComponentType { - return null - } + abstract getComponentType (): ComponentType getRecoveryToken (): any { return null diff --git a/app/src/services/config.ts b/app/src/services/config.ts index 94ca0855..4b2561b9 100644 --- a/app/src/services/config.ts +++ b/app/src/services/config.ts @@ -30,6 +30,11 @@ export interface IConfigData { @Injectable() export class ConfigService { + store: IConfigData + change = new EventEmitter() + restartRequested: boolean + private path: string + constructor ( electron: ElectronService ) { @@ -37,10 +42,6 @@ export class ConfigService { this.load() } - private path: string - store: IConfigData - change = new EventEmitter() - load () { if (fs.existsSync(this.path)) { this.store = configMerge(defaultConfigStructure, yaml.safeLoad(fs.readFileSync(this.path, 'utf8'))) @@ -61,4 +62,8 @@ export class ConfigService { emitChange () { this.change.emit() } + + requestRestart () { + this.restartRequested = true + } } diff --git a/app/src/services/plugins.ts b/app/src/services/plugins.ts index 9ed51900..cd35d71b 100644 --- a/app/src/services/plugins.ts +++ b/app/src/services/plugins.ts @@ -1,44 +1,21 @@ import { Injectable } from '@angular/core' -interface IPluginEntry { - plugin: any - weight: number +class Plugin { + ngModule: any + name: string } + @Injectable() export class PluginsService { - plugins: {[type: string]: IPluginEntry[]} = {} + plugins: Plugin[] = [] - constructor ( - ) { + register (plugin: Plugin): void { + this.plugins.push(plugin) } - register (type: string, plugin: any, weight = 0): void { - if (!this.plugins[type]) { - this.plugins[type] = [] - } - this.plugins[type].push({ plugin, weight }) - } - - getAll (type: string): T[] { - let plugins = this.plugins[type] || [] - plugins = plugins.sort((a: IPluginEntry, b: IPluginEntry) => { - if (a.weight < b.weight) { - return -1 - } else if (a.weight > b.weight) { - return 1 - } - return 0 - }) - return plugins.map((x) => (x.plugin)) - } - - emit (type: string, event: string, parameters: any[]) { - (this.plugins[type] || []).forEach((entry) => { - if (entry.plugin[event]) { - entry.plugin[event].bind(entry.plugin)(parameters) - } - }) + getModules (): any[] { + return this.plugins.map((plugin) => plugin.ngModule) } } diff --git a/app/src/settings/api.ts b/app/src/settings/api.ts new file mode 100644 index 00000000..9ed83161 --- /dev/null +++ b/app/src/settings/api.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core' + +export declare type ComponentType = new (...args: any[]) => Component + +export abstract class SettingsProvider { + title: string + + getComponentType (): ComponentType { + return null + } +} diff --git a/app/src/settings/components/settingsPane.less b/app/src/settings/components/settingsPane.less index 7f4abb6a..5da83ebe 100644 --- a/app/src/settings/components/settingsPane.less +++ b/app/src/settings/components/settingsPane.less @@ -1,7 +1,7 @@ :host { flex: auto; margin: 15px; - + >.btn-block { margin-bottom: 20px; } @@ -9,14 +9,4 @@ >.modal-body { padding: 0 0 20px !important; } - - .appearance-preview { - background: black; - padding: 10px 20px; - margin: 0 0 10px; - - .text { - color: white; - } - } } diff --git a/app/src/settings/components/settingsPane.pug b/app/src/settings/components/settingsPane.pug index 5df3513f..71cf18c1 100644 --- a/app/src/settings/components/settingsPane.pug +++ b/app/src/settings/components/settingsPane.pug @@ -1,6 +1,11 @@ -button.btn.btn-outline-warning.btn-block(*ngIf='restartRequested', '(click)'='restartApp()') Restart the app to apply changes +button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(click)'='restartApp()') Restart the app to apply changes ngb-tabset(type='tabs') + ngb-tab(*ngFor='let provider of settingsProviders') + template(ngbTabTitle) + | {{provider.title}} + template(ngbTabContent) + settings-tab-body([provider]='provider') ngb-tab template(ngbTabTitle) | Application @@ -124,71 +129,6 @@ ngb-tabset(type='tabs') step='0.01' ) - ngb-tab - template(ngbTabTitle) - | Appearance - template(ngbTabContent) - .row - .col-sm-6 - .form-group - label Preview - .appearance-preview( - [style.font-family]='config.store.appearance.font', - [style.font-size]='config.store.appearance.fontSize + "px"', - ) - .text john@doe-pc$ ls - .text foo bar - .col-sm-6 - .form-group - label Font - input.form-control( - type='text', - [ngbTypeahead]='fontAutocomplete', - '[(ngModel)]'='config.store.appearance.font', - '(ngModelChange)'='config.save()', - ) - small.form-text.text-muted Font to be used in the terminal - - .form-group - label Font size - input.form-control( - type='number', - '[(ngModel)]'='config.store.appearance.fontSize', - '(ngModelChange)'='config.save()', - ) - small.form-text.text-muted Text size to be used in the terminal - - ngb-tab - template(ngbTabTitle) - | Terminal - template(ngbTabContent) - .form-group - label Terminal bell - br - div( - '[(ngModel)]'='config.store.terminal.bell' - '(ngModelChange)'='config.save()' - ngbRadioGroup - ) - label.btn.btn-secondary - input( - type='radio', - [value]='"off"' - ) - | Off - label.btn.btn-secondary - input( - type='radio', - [value]='"sound"' - ) - | Sound - label.btn.btn-secondary - input( - type='radio', - [value]='"notification"' - ) - | Notification - ngb-tab template(ngbTabTitle) | Hotkeys diff --git a/app/src/settings/components/settingsPane.ts b/app/src/settings/components/settingsPane.ts index 1f7afd68..34fa5882 100644 --- a/app/src/settings/components/settingsPane.ts +++ b/app/src/settings/components/settingsPane.ts @@ -1,16 +1,11 @@ -import { Component } from '@angular/core' +import { Component, Inject } from '@angular/core' import { ElectronService } from 'services/electron' -import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp' import { ConfigService } from 'services/config' import { DockingService } from 'services/docking' -import { Observable } from 'rxjs/Observable' -import 'rxjs/add/operator/map' -import 'rxjs/add/operator/debounceTime' -import 'rxjs/add/operator/distinctUntilChanged' -const childProcessPromise = nodeRequire('child-process-promise') import { BaseTabComponent } from 'components/baseTab' import { SettingsTab } from '../tab' +import { SettingsProvider } from '../api' @Component({ @@ -19,56 +14,21 @@ import { SettingsTab } from '../tab' styles: [require('./settingsPane.less')], }) export class SettingsPaneComponent extends BaseTabComponent { - isWindows: boolean - isMac: boolean - isLinux: boolean - year: number - version: string - fonts: string[] = [] - restartRequested: boolean globalHotkey = ['Ctrl+Shift+G'] constructor( public config: ConfigService, private electron: ElectronService, public docking: DockingService, - hostApp: HostAppService, + @Inject(SettingsProvider) public settingsProviders: SettingsProvider[] ) { super() - this.isWindows = hostApp.platform == PLATFORM_WINDOWS - this.isMac = hostApp.platform == PLATFORM_MAC - this.isLinux = hostApp.platform == PLATFORM_LINUX - this.version = electron.app.getVersion() - this.year = new Date().getFullYear() - } - - ngOnInit () { - childProcessPromise.exec('fc-list :spacing=mono').then((result) => { - this.fonts = result.stdout - .split('\n') - .filter((x) => !!x) - .map((x) => x.split(':')[1].trim()) - .map((x) => x.split(',')[0].trim()) - this.fonts.sort() - }) - } - - fontAutocomplete = (text$: Observable) => { - return text$ - .debounceTime(200) - .distinctUntilChanged() - .map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))) - .map(list => Array.from(new Set(list))) } ngOnDestroy () { this.config.save() } - requestRestart () { - this.restartRequested = true - } - restartApp () { this.electron.app.relaunch() this.electron.app.exit() diff --git a/app/src/settings/components/settingsTabBody.ts b/app/src/settings/components/settingsTabBody.ts new file mode 100644 index 00000000..a3ea6cff --- /dev/null +++ b/app/src/settings/components/settingsTabBody.ts @@ -0,0 +1,25 @@ +import { Component, Input, ViewContainerRef, ViewChild, ComponentFactoryResolver, ComponentRef } from '@angular/core' +import { SettingsProvider } from '../api' + +@Component({ + selector: 'settings-tab-body', + template: '', +}) +export class SettingsTabBodyComponent { + @Input() provider: SettingsProvider + @ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef + private component: ComponentRef + + constructor (private componentFactoryResolver: ComponentFactoryResolver) { } + + ngAfterViewInit () { + // run after the change detection finishes + setImmediate(() => { + this.component = this.placeholder.createComponent( + this.componentFactoryResolver.resolveComponentFactory( + this.provider.getComponentType() + ) + ) + }) + } +} diff --git a/app/src/settings/index.ts b/app/src/settings/index.ts index d8561f0c..2fdedd6a 100644 --- a/app/src/settings/index.ts +++ b/app/src/settings/index.ts @@ -8,6 +8,7 @@ import { HotkeyDisplayComponent } from './components/hotkeyDisplay' import { HotkeyHintComponent } from './components/hotkeyHint' import { HotkeyInputModalComponent } from './components/hotkeyInputModal' import { SettingsPaneComponent } from './components/settingsPane' +import { SettingsTabBodyComponent } from './components/settingsTabBody' import { ToolbarButtonProvider, TabRecoveryProvider } from 'api' @@ -35,6 +36,7 @@ import { RecoveryProvider } from './recoveryProvider' HotkeyInputComponent, HotkeyInputModalComponent, SettingsPaneComponent, + SettingsTabBodyComponent, ], }) class SettingsModule { diff --git a/app/src/terminal/components/settings.pug b/app/src/terminal/components/settings.pug new file mode 100644 index 00000000..c56d33b8 --- /dev/null +++ b/app/src/terminal/components/settings.pug @@ -0,0 +1,56 @@ +.row + .col-sm-6 + .form-group + label Preview + .appearance-preview( + [style.font-family]='config.full().appearance.font', + [style.font-size]='config.full().appearance.fontSize + "px"', + ) + .text john@doe-pc$ ls + .text foo bar + .col-sm-6 + .form-group + label Font + input.form-control( + type='text', + [ngbTypeahead]='fontAutocomplete', + '[(ngModel)]'='config.store.appearance.font', + (ngModelChange)='config.save()', + ) + small.form-text.text-muted Font to be used in the terminal + + .form-group + label Font size + input.form-control( + type='number', + '[(ngModel)]'='config.store.appearance.fontSize', + (ngModelChange)='config.save()', + ) + small.form-text.text-muted Text size to be used in the terminal + + .form-group + label Terminal bell + br + div( + '[(ngModel)]'='config.store.terminal.bell', + (ngModelChange)='config.save()', + ngbRadioGroup + ) + label.btn.btn-secondary + input( + type='radio', + [value]='"off"' + ) + | Off + label.btn.btn-secondary + input( + type='radio', + [value]='"sound"' + ) + | Sound + label.btn.btn-secondary + input( + type='radio', + [value]='"notification"' + ) + | Notification diff --git a/app/src/terminal/components/settings.scss b/app/src/terminal/components/settings.scss new file mode 100644 index 00000000..c085ce3a --- /dev/null +++ b/app/src/terminal/components/settings.scss @@ -0,0 +1,9 @@ +.appearance-preview { + background: black; + padding: 10px 20px; + margin: 0 0 10px; + + .text { + color: white; + } +} diff --git a/app/src/terminal/components/settings.ts b/app/src/terminal/components/settings.ts new file mode 100644 index 00000000..d6ce6d34 --- /dev/null +++ b/app/src/terminal/components/settings.ts @@ -0,0 +1,42 @@ +import { Observable } from 'rxjs/Observable' +import 'rxjs/add/operator/map' +import 'rxjs/add/operator/debounceTime' +import 'rxjs/add/operator/distinctUntilChanged' +import { Component } from '@angular/core' +const childProcessPromise = nodeRequire('child-process-promise') + +import { ConfigService } from 'services/config' + + +@Component({ + template: require('./settings.pug'), + styles: [require('./settings.scss')], +}) +export class SettingsComponent { + fonts: string[] = [] + + constructor( + public config: ConfigService, + ) { } + + ngOnInit () { + childProcessPromise.exec('fc-list :spacing=mono').then((result) => { + this.fonts = result.stdout + .split('\n') + .filter((x) => !!x) + .map((x) => x.split(':')[1].trim()) + .map((x) => x.split(',')[0].trim()) + this.fonts.sort() + }) + } + + fontAutocomplete = (text$: Observable) => { + return text$ + .debounceTime(200) + .distinctUntilChanged() + .map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v))) + .map(list => Array.from(new Set(list))) + } + + +} diff --git a/app/src/terminal/index.ts b/app/src/terminal/index.ts index 846c816d..4e6689f8 100644 --- a/app/src/terminal/index.ts +++ b/app/src/terminal/index.ts @@ -1,33 +1,40 @@ import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' +import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { ToolbarButtonProvider, TabRecoveryProvider } from 'api' +import { SettingsProvider } from '../settings/api' import { TerminalTabComponent } from './components/terminalTab' +import { SettingsComponent } from './components/settings' import { SessionsService } from './services/sessions' import { ScreenPersistenceProvider } from './persistenceProviders' import { ButtonProvider } from './buttonProvider' import { RecoveryProvider } from './recoveryProvider' import { SessionPersistenceProvider } from './api' - +import { TerminalSettingsProvider } from './settings' @NgModule({ imports: [ BrowserModule, FormsModule, + NgbModule, ], providers: [ { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, { provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true }, SessionsService, { provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider }, + { provide: SettingsProvider, useClass: TerminalSettingsProvider, multi: true }, ], entryComponents: [ TerminalTabComponent, + SettingsComponent, ], declarations: [ TerminalTabComponent, + SettingsComponent, ], }) class TerminalModule { diff --git a/app/src/terminal/settings.ts b/app/src/terminal/settings.ts new file mode 100644 index 00000000..e0c69355 --- /dev/null +++ b/app/src/terminal/settings.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core' +import { SettingsProvider, ComponentType } from '../settings/api' +import { SettingsComponent } from './components/settings' + + +@Injectable() +export class TerminalSettingsProvider extends SettingsProvider { + title = 'Terminal' + + getComponentType (): ComponentType { + return SettingsComponent + } +}