feat: add tray
@@ -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) => {
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -16,6 +16,7 @@ export default {
|
||||
'hide': '隐藏 Motrix',
|
||||
'hide-others': '隐藏其他',
|
||||
'unhide': '显示全部',
|
||||
'show': '显示 Motrix',
|
||||
'quit': '退出 Motrix',
|
||||
'under-development-message': '该功能开发中...',
|
||||
'yes': '是',
|
||||
|
||||
@@ -16,6 +16,7 @@ export default {
|
||||
'hide': '隱藏 Motrix',
|
||||
'hide-others': '隱藏其它',
|
||||
'unhide': '全部顯示',
|
||||
'show': '顯示 Motrix',
|
||||
'quit': '結束 Motrix',
|
||||
'under-development-message': '該功能開發中...',
|
||||
'yes': '是',
|
||||
|
||||
|
After Width: | Height: | Size: 140 B |
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 137 B |
|
After Width: | Height: | Size: 175 B |