diff --git a/src/main/Application.js b/src/main/Application.js index 359872e..c6af36b 100644 --- a/src/main/Application.js +++ b/src/main/Application.js @@ -13,6 +13,7 @@ import ProtocolManager from './core/ProtocolManager' import WindowManager from './ui/WindowManager' import MenuManager from './ui/MenuManager' import TouchBarManager from './ui/TouchBarManager' +import TrayManager from './ui/TrayManager' export default class Application extends EventEmitter { constructor () { @@ -43,6 +44,8 @@ export default class Application extends EventEmitter { this.touchBarManager = new TouchBarManager() + this.trayManager = new TrayManager() + this.energyManager = new EnergyManager() this.initUpdaterManager() @@ -248,7 +251,12 @@ export default class Application extends EventEmitter { }) this.on('application:change-locale', (locale) => { - this.menuManager.setup(locale) + logger.info('[Motrix] application:change-locale===>', locale) + this.localeManager.changeLanguageByLocale(locale) + .then(() => { + this.menuManager.setup(locale) + this.trayManager.setup(locale) + }) }) this.on('application:open-file', (event) => { diff --git a/src/main/menus/tray.json b/src/main/menus/tray.json new file mode 100644 index 0000000..1c72fea --- /dev/null +++ b/src/main/menus/tray.json @@ -0,0 +1,11 @@ +[ + { "id": "task.new-task", "command": "application:new-task", "command-after": "application:show,index" }, + { "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": "torrent", "command-after": "application:show,index" }, + { "id": "task.open-file", "command": "application:open-file", "command-before": "application:show,index" }, + { "type": "separator" }, + { "id": "app.show", "command": "application:show", "command-arg": "index" }, + { "id": "help.manual", "command": "help:manual" }, + { "type": "separator" }, + { "id": "app.preferences", "command": "application:preferences", "command-before": "application:show,index" }, + { "id": "app.quit", "role": "quit" } +] diff --git a/src/main/ui/MenuManager.js b/src/main/ui/MenuManager.js index 28f2e1a..fab9f17 100644 --- a/src/main/ui/MenuManager.js +++ b/src/main/ui/MenuManager.js @@ -6,29 +6,24 @@ import { updateStates } from '../utils/menu' import keymap from '@shared/keymap' +import { getI18n } from '@/ui/Locale' export default class MenuManager extends EventEmitter { constructor (options) { super() this.options = options + this.i18n = getI18n() this.keymap = keymap - this.template = [] - - this.menu = null this.items = {} + + this.load() + + this.setup() } - load (locale = 'en-US') { - let template = null - try { - template = require(`../menus/${locale}/${process.platform}.json`) - if (!template) { - template = require(`../menus/en-US/${process.platform}.json`) - } - } catch (err) { - template = require(`../menus/en-US/${process.platform}.json`) - } + load () { + let template = require(`../menus/${process.platform}.json`) this.template = template['menu'] } @@ -38,15 +33,21 @@ export default class MenuManager extends EventEmitter { keystrokesByCommand[this.keymap[item]] = item } - const tpl = translateTemplate(this.template, keystrokesByCommand) - this.menu = Menu.buildFromTemplate(tpl) + // Deepclone the menu template to refresh menu + const template = JSON.parse(JSON.stringify(this.template)) + const tpl = translateTemplate(template, keystrokesByCommand, this.i18n) + const menu = Menu.buildFromTemplate(tpl) + return menu } - setup (locale) { - this.load(locale) - this.build() - Menu.setApplicationMenu(this.menu) - this.items = flattenMenuItems(this.menu) + setup () { + const menu = this.build() + Menu.setApplicationMenu(menu) + this.items = flattenMenuItems(menu) + } + + rebuild () { + this.setup() } updateStates (visibleStates, enabledStates, checkedStates) { diff --git a/src/main/ui/TrayManager.js b/src/main/ui/TrayManager.js index ec7601c..c1178bf 100644 --- a/src/main/ui/TrayManager.js +++ b/src/main/ui/TrayManager.js @@ -1,3 +1,100 @@ -export default class TrayManager { +import { EventEmitter } from 'events' +import { join } from 'path' +import { Tray, Menu } from 'electron' +import is from 'electron-is' +import { translateTemplate } from '../utils/menu' +import { getI18n } from '@/ui/Locale' +let tray = null + +export default class TrayManager extends EventEmitter { + constructor (options = {}) { + super() + + this.i18n = getI18n() + + this.menu = null + + this.load() + this.init() + this.setup() + this.handleEvents() + } + + load () { + this.template = require(`../menus/tray.json`) + + if (is.windows()) { + this.normalIcon = join(__static, './mo-tray-colorful-normal.ico') + this.activeIcon = join(__static, './mo-tray-colorful-active.ico') + } else if (is.macOS()) { + this.normalIcon = join(__static, './mo-tray-normal.png') + this.activeIcon = join(__static, './mo-tray-active.png') + } else { + this.normalIcon = join(__static, './mo-tray-colorful-normal.png') + this.activeIcon = join(__static, './mo-tray-colorful-active.png') + } + } + + build () { + const keystrokesByCommand = {} + for (let item in this.keymap) { + keystrokesByCommand[this.keymap[item]] = item + } + + // Deepclone the menu template to refresh menu + const template = JSON.parse(JSON.stringify(this.template)) + const tpl = translateTemplate(template, keystrokesByCommand, this.i18n) + this.menu = Menu.buildFromTemplate(tpl) + } + + setup () { + this.build() + + /** + * Linux requires setContextMenu to be called + * in order for the context menu to populate correctly + */ + if (process.platform === 'linux') { + tray.setContextMenu(this.menu) + } + } + + init () { + tray = new Tray(this.normalIcon) + tray.setToolTip('Motrix') + } + + handleEvents () { + tray.on('click', this.handleTrayClick) + tray.on('double-click', this.handleTrayDbClick) + tray.on('right-click', this.handleTrayRightClick) + + tray.on('drop-files', this.handleTrayDropFile) + } + + handleTrayClick = (event) => { + event.preventDefault() + global.application.toggle() + } + + handleTrayDbClick = (event) => { + event.preventDefault() + global.application.show() + } + + handleTrayRightClick = (event) => { + event.preventDefault() + tray.popUpContextMenu(this.menu) + } + + handleTrayDropFile = (event, files) => { + global.application.show() + global.application.handleFile(files[0]) + } + + updateStatus (status) { + const icon = status === 'active' ? this.activeIcon : this.normalIcon + tray.setImage(icon) + } } diff --git a/src/main/utils/menu.js b/src/main/utils/menu.js index 70a9fbf..13e883d 100644 --- a/src/main/utils/menu.js +++ b/src/main/utils/menu.js @@ -71,19 +71,29 @@ function findById (template, id) { return null } -export function translateTemplate (template, keystrokesByCommand) { +export function translateTemplate (template, keystrokesByCommand, i18n) { for (let i in template) { let item = template[i] if (item.command) { item.accelerator = acceleratorForCommand(item.command, keystrokesByCommand) } + + // If label is specified, label is used as the key of i18n.t(key), + // which mainly solves the inaccurate translation of item.id. + if (i18n) { + if (item.label) { + item.label = i18n.t(item.label) + } else if (item.id) { + item.label = i18n.t(item.id) + } + } + item.click = () => { - console.log('click sendCommand', item) handleCommand(item) } if (item.submenu) { - translateTemplate(item.submenu, keystrokesByCommand) + translateTemplate(item.submenu, keystrokesByCommand, i18n) } } return template diff --git a/src/shared/locales/de/app.js b/src/shared/locales/de/app.js index f6b0c2c..3c9782a 100644 --- a/src/shared/locales/de/app.js +++ b/src/shared/locales/de/app.js @@ -16,6 +16,7 @@ export default { 'hide': 'Motrix verbergen', 'hide-others': 'Andere verbergen', 'unhide': 'Alles anzeigen', + 'show': 'Motrix anzeigen', 'quit': 'Motrix beenden', 'under-development-message': 'Entschuldigung, diese Funktion befindet sich in der Entwicklung...', 'yes': 'Ja', diff --git a/src/shared/locales/en-US/app.js b/src/shared/locales/en-US/app.js index 6ea8bf5..1dae941 100644 --- a/src/shared/locales/en-US/app.js +++ b/src/shared/locales/en-US/app.js @@ -16,6 +16,7 @@ export default { 'hide': 'Hide Motrix', 'hide-others': 'Hide Others', 'unhide': 'Show All', + 'show': 'Show Motrix', 'quit': 'Quit Motrix', 'under-development-message': 'Sorry, this feature is under development...', 'yes': 'Yes', diff --git a/src/shared/locales/fr/app.js b/src/shared/locales/fr/app.js index 1e6da12..4bb0bfa 100644 --- a/src/shared/locales/fr/app.js +++ b/src/shared/locales/fr/app.js @@ -16,6 +16,7 @@ export default { 'hide': 'Cacher Motrix', 'hide-others': 'Cacher les autres', 'unhide': 'Tout montrer', + 'show': 'Montrer Motrix', 'quit': 'Quitter Motrix', 'under-development-message': 'Désolé, cette fonctionnalité est en cours de développement...', 'yes': 'Oui', diff --git a/src/shared/locales/pt-BR/app.js b/src/shared/locales/pt-BR/app.js index c798ada..912a684 100644 --- a/src/shared/locales/pt-BR/app.js +++ b/src/shared/locales/pt-BR/app.js @@ -16,6 +16,7 @@ export default { 'hide': 'Ocultar Motrix', 'hide-others': 'Ocultar Outros', 'unhide': 'Exibir Todos', + 'show': 'Exibir Motrix', 'quit': 'Sair do Motrix', 'under-development-message': 'Desculpe, essa funcionalidade está em desenvolvimento...', 'yes': 'Sim', diff --git a/src/shared/locales/tr/app.js b/src/shared/locales/tr/app.js index d90025b..61350a1 100644 --- a/src/shared/locales/tr/app.js +++ b/src/shared/locales/tr/app.js @@ -16,6 +16,7 @@ export default { 'hide': 'Motrix\'i gizle', 'hide-others': 'Diğerlerini gizle', 'unhide': 'Hepsini Göster', + 'show': 'Motrix\'i Göster', 'quit': 'Motrix\'ten çık', 'under-development-message': 'Üzgünüz, bu özellik geliştirme aşamasında...', 'yes': 'Evet', diff --git a/src/shared/locales/zh-CN/app.js b/src/shared/locales/zh-CN/app.js index 543f499..98c3854 100644 --- a/src/shared/locales/zh-CN/app.js +++ b/src/shared/locales/zh-CN/app.js @@ -16,6 +16,7 @@ export default { 'hide': '隐藏 Motrix', 'hide-others': '隐藏其他', 'unhide': '显示全部', + 'show': '显示 Motrix', 'quit': '退出 Motrix', 'under-development-message': '该功能开发中...', 'yes': '是', diff --git a/src/shared/locales/zh-TW/app.js b/src/shared/locales/zh-TW/app.js index 0846664..0c8d549 100644 --- a/src/shared/locales/zh-TW/app.js +++ b/src/shared/locales/zh-TW/app.js @@ -16,6 +16,7 @@ export default { 'hide': '隱藏 Motrix', 'hide-others': '隱藏其它', 'unhide': '全部顯示', + 'show': '顯示 Motrix', 'quit': '結束 Motrix', 'under-development-message': '該功能開發中...', 'yes': '是', diff --git a/static/mo-tray-active.png b/static/mo-tray-active.png new file mode 100755 index 0000000..6511cf0 Binary files /dev/null and b/static/mo-tray-active.png differ diff --git a/static/mo-tray-active@2x.png b/static/mo-tray-active@2x.png new file mode 100755 index 0000000..9583cc3 Binary files /dev/null and b/static/mo-tray-active@2x.png differ diff --git a/static/mo-tray-colorful-active.ico b/static/mo-tray-colorful-active.ico new file mode 100644 index 0000000..720794c Binary files /dev/null and b/static/mo-tray-colorful-active.ico differ diff --git a/static/mo-tray-colorful-active.png b/static/mo-tray-colorful-active.png new file mode 100644 index 0000000..7ba2ea1 Binary files /dev/null and b/static/mo-tray-colorful-active.png differ diff --git a/static/mo-tray-colorful-normal.ico b/static/mo-tray-colorful-normal.ico new file mode 100644 index 0000000..6be8a29 Binary files /dev/null and b/static/mo-tray-colorful-normal.ico differ diff --git a/static/mo-tray-colorful-normal.png b/static/mo-tray-colorful-normal.png new file mode 100644 index 0000000..cc4f989 Binary files /dev/null and b/static/mo-tray-colorful-normal.png differ diff --git a/static/mo-tray-normal.png b/static/mo-tray-normal.png new file mode 100755 index 0000000..5b957f7 Binary files /dev/null and b/static/mo-tray-normal.png differ diff --git a/static/mo-tray-normal@2x.png b/static/mo-tray-normal@2x.png new file mode 100755 index 0000000..2b9af30 Binary files /dev/null and b/static/mo-tray-normal@2x.png differ