app.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { app, ipcMain, Menu, Tray, shell } from 'electron'
  2. import * as electron from 'electron'
  3. import { loadConfig } from './config'
  4. import { Window, WindowOptions } from './window'
  5. export class Application {
  6. private tray: Tray
  7. private windows: Window[] = []
  8. constructor () {
  9. ipcMain.on('app:config-change', () => {
  10. this.broadcast('host:config-change')
  11. })
  12. const configData = loadConfig()
  13. if (process.platform === 'linux' && ((configData.appearance || {}).opacity || 1) !== 1) {
  14. app.commandLine.appendSwitch('enable-transparent-visuals')
  15. app.disableHardwareAcceleration()
  16. }
  17. app.commandLine.appendSwitch('disable-http-cache')
  18. app.commandLine.appendSwitch('lang', 'EN')
  19. for (const flag of configData.flags || [['force_discrete_gpu', '0']]) {
  20. app.commandLine.appendSwitch(flag[0], flag[1])
  21. }
  22. }
  23. init () {
  24. electron.screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
  25. }
  26. async newWindow (options?: WindowOptions): Promise<Window> {
  27. let window = new Window(options)
  28. this.windows.push(window)
  29. window.visible$.subscribe(visible => {
  30. if (visible) {
  31. this.disableTray()
  32. } else {
  33. this.enableTray()
  34. }
  35. })
  36. if (process.platform === 'darwin') {
  37. this.setupMenu()
  38. }
  39. await window.ready
  40. return window
  41. }
  42. broadcast (event, ...args) {
  43. for (let window of this.windows) {
  44. window.send(event, ...args)
  45. }
  46. }
  47. async send (event, ...args) {
  48. if (!this.hasWindows()) {
  49. await this.newWindow()
  50. }
  51. this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args)
  52. }
  53. enableTray () {
  54. if (this.tray) {
  55. return
  56. }
  57. if (process.platform === 'darwin') {
  58. this.tray = new Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
  59. this.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
  60. } else {
  61. this.tray = new Tray(`${app.getAppPath()}/assets/tray.png`)
  62. }
  63. this.tray.on('click', () => setTimeout(() => this.focus()));
  64. const contextMenu = Menu.buildFromTemplate([{
  65. label: 'Show',
  66. click: () => this.focus(),
  67. }])
  68. if (process.platform !== 'darwin') {
  69. this.tray.setContextMenu(contextMenu)
  70. }
  71. this.tray.setToolTip(`Terminus ${app.getVersion()}`)
  72. }
  73. disableTray () {
  74. if (this.tray) {
  75. this.tray.destroy()
  76. this.tray = null
  77. }
  78. }
  79. hasWindows () {
  80. return !!this.windows.length
  81. }
  82. focus () {
  83. for (let window of this.windows) {
  84. window.show()
  85. }
  86. }
  87. private setupMenu () {
  88. let template: Electron.MenuItemConstructorOptions[] = [
  89. {
  90. label: 'Application',
  91. submenu: [
  92. { role: 'about', label: 'About Terminus' },
  93. { type: 'separator' },
  94. {
  95. label: 'Preferences',
  96. accelerator: 'Cmd+,',
  97. click: async () => {
  98. if (!this.hasWindows()) {
  99. await this.newWindow()
  100. }
  101. this.windows[0].send('host:preferences-menu')
  102. },
  103. },
  104. { type: 'separator' },
  105. { role: 'services', submenu: [] },
  106. { type: 'separator' },
  107. { role: 'hide' },
  108. { role: 'hideOthers' },
  109. { role: 'unhide' },
  110. { type: 'separator' },
  111. {
  112. label: 'Quit',
  113. accelerator: 'Cmd+Q',
  114. click () {
  115. app.quit()
  116. },
  117. },
  118. ],
  119. },
  120. {
  121. label: 'Edit',
  122. submenu: [
  123. { role: 'undo' },
  124. { role: 'redo' },
  125. { type: 'separator' },
  126. { role: 'cut' },
  127. { role: 'copy' },
  128. { role: 'paste' },
  129. { role: 'pasteAndMatchStyle' },
  130. { role: 'delete' },
  131. { role: 'selectAll' },
  132. ],
  133. },
  134. {
  135. label: 'View',
  136. submenu: [
  137. { role: 'reload' },
  138. { role: 'forceReload' },
  139. { role: 'toggleDevTools' },
  140. { type: 'separator' },
  141. { role: 'resetZoom' },
  142. { role: 'zoomIn' },
  143. { role: 'zoomOut' },
  144. { type: 'separator' },
  145. { role: 'togglefullscreen' },
  146. ],
  147. },
  148. {
  149. role: 'window',
  150. submenu: [
  151. { role: 'minimize' },
  152. { role: 'zoom' },
  153. { type: 'separator' },
  154. { role: 'front' },
  155. ],
  156. },
  157. {
  158. role: 'help',
  159. submenu: [
  160. {
  161. label: 'Website',
  162. click () {
  163. shell.openExternal('https://eugeny.github.io/terminus')
  164. },
  165. },
  166. ],
  167. }
  168. ]
  169. Menu.setApplicationMenu(Menu.buildFromTemplate(template))
  170. }
  171. }