This commit is contained in:
Eugene Pankov 2017-03-26 22:39:10 +02:00
parent c9ead5e93d
commit 482343e383
14 changed files with 197 additions and 161 deletions

View file

@ -3,7 +3,8 @@ import { BaseTabComponent } from 'components/baseTab'
export declare type ComponentType<T extends Tab> = new (...args: any[]) => BaseTabComponent<T>
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<Tab> {
return null
}
abstract getComponentType (): ComponentType<Tab>
getRecoveryToken (): any {
return null

View file

@ -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
}
}

View file

@ -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<T> (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) => <T>(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)
}
}

11
app/src/settings/api.ts Normal file
View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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<SettingsTab> {
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<string>) => {
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()

View file

@ -0,0 +1,25 @@
import { Component, Input, ViewContainerRef, ViewChild, ComponentFactoryResolver, ComponentRef } from '@angular/core'
import { SettingsProvider } from '../api'
@Component({
selector: 'settings-tab-body',
template: '<template #placeholder></template>',
})
export class SettingsTabBodyComponent {
@Input() provider: SettingsProvider
@ViewChild('placeholder', {read: ViewContainerRef}) placeholder: ViewContainerRef
private component: ComponentRef<Component>
constructor (private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterViewInit () {
// run after the change detection finishes
setImmediate(() => {
this.component = this.placeholder.createComponent(
this.componentFactoryResolver.resolveComponentFactory(
this.provider.getComponentType()
)
)
})
}
}

View file

@ -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 {

View file

@ -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

View file

@ -0,0 +1,9 @@
.appearance-preview {
background: black;
padding: 10px 20px;
margin: 0 0 10px;
.text {
color: white;
}
}

View file

@ -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<string>) => {
return text$
.debounceTime(200)
.distinctUntilChanged()
.map(query => this.fonts.filter(v => new RegExp(query, 'gi').test(v)))
.map(list => Array.from(new Set(list)))
}
}

View file

@ -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 {

View file

@ -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
}
}