跳转到主内容

辅助工具

· 阅读时间:约 3 分钟

创建具有辅助功能的应用程序是很重要的,我们很乐意介绍DevtronSpectron,这两个新功能能让开发者们有机会让它们的应用程序对每个人都更加可用。


Electron 应用中有关辅助功能的开发和网站是相似的因为两者最终使用的都是HTML. 然而, 对于Electron应用, 你不能使用在线的辅助功能审查者, 因为你的应用没有一个URL可以提供给审查者.

这些新功能将这些审计工具带到您的Electron应用程序中。 您可以选择使用 Spectron 将审计工具添加到测试中,也可以在 DevTron 的 DevTools 中使用它们。 继续阅读可简要了解这两个工具或阅读 辅助功能文档 以获取更加详细的信息。

Spectron

在测试框架Spectron中,你可以审查应用程序中的每个 window 和 <webview> 标签。 例如:

app.client.auditAccessibility().then(function (audit) {
if (audit.failed) {
console.error(audit.message);
}
});

你可以从这里Spectron文档阅读到更多有关于这个功能的信息。

Devtron

在 Devtron 中, 有一个新的辅助功能选项卡, 允许您对应用程序中的某一个页面进行审核, 并对审核结果进行排序和筛选。

devtron 截图

这两种工具都使用了Google 为 Chrome 所构建的 辅助功能开发工具 库。 您可以在该 repository's wiki 上更加详细的了解这个库使用了哪些辅助功能审核规则。

如果您知道其他很好的Electron辅助功能工具, 请创建一个pull request来将它们添加到 辅助功能文档 中。

npm install electron

· 阅读时间:约 4 分钟

Electron 从 1.3.1 版本开始,可以使用 npm install electron --save-dev 安装最新的预编译版本的 Electron 到你的应用程序中。


npm install electron

预编译的 Electron 二进制分发文件

如果你在此前使用过一些 Electron 构建的应用程序,你大概率会接触到 electron-prebuilt 这个 npm 软件包。 这个软件包对于每个 Electron 项目来说几乎都是不可或缺的。 当你安装这个软件包时,它会检测你的操作系统,然后下载适合于你的系统的二进制分发文件。

新的名字

Electron 的安装过程对于初学者来说几乎是一道高墙。 许多勇敢的人在初次使用 Electron 开发应用程序时往往会试图运行 npm install electron 安装 Electron,然而正确的做法却是反直觉的 npm install electron-prebuilt,他们往往在经历了许多混乱之后才会意识到这不是他们想要的 electron

这是因为在 npm 上已经有名为 electron 的项目,在 GitHub 的 Electron 项目创建之前就早已存在。 为了让 Electron 的开发更加容易入门且直观,我们联系了目前 electron 这个 npm 软件包的拥有者,询问他是否愿意将名字转让给我们。 幸运的是,他是我们项目的粉丝,并且同意了调整名字的提案。

prebuilt 依旧存在

从 1.3.1 版本开始,我们同时发布了 electronelectron-prebuilt 两个软件包到 npm 上。 这两个软件包是完全一致的。 由于现在有许多开发者还在他们的项目里使用 electron-prebuilt 这个名字,在未来的一段时间内,我们还会继续使用这两个名字发布软件包。 我们推荐更新你的package.json文件去使用新的electron依赖,但是我们会持续发布新的electron-prebuilt 直到2016年底。

electron-userland/electron-prebuilt 仓库将继续作为 electron 软件包存在。

致谢

我们特别感谢 @mafintosh@maxogden 和许多 贡献者,他们创建并维护了 electron-prebuilt 项目,并且不懈服务 JavaScript、Node.js 和 Electron 社区。

此外,我们也对转让了 electron 包名的 @logicalparadox 致以诚挚的谢意。

更新你的项目

我们与社区一起努力更新了受此次变更影响的一些热门软件包。 electron-packagerelectron-rebuildelectron-builder 等软件包已经支持了新名字,同时保留了对旧名字的支持。

如果你在使用这些新软件包的时候发现任何问题,请务必在 electron-userland/electron-prebuilt 开启一个 issue 告知我们。

对于 Electron 相关的其他问题,请在 electron/electron 仓库开启 issue。

Electron Internals: Using Node as a Library

· 阅读时间:约 5 分钟

This is the second post in an ongoing series explaining the internals of Electron. Check out the first post about event loop integration if you haven't already.

Most people use Node for server-side applications, but because of Node's rich API set and thriving community, it is also a great fit for an embedded library. This post explains how Node is used as a library in Electron.


构建系统

节点和 Electron 都使用 GYP 作为他们的构建系统。 If you want to embed Node inside your app, you have to use it as your build system too.

New to GYP? 新建 GYP? Read this guide before you continue further in this post.

Node's flags

个节点。 yp 节点源代码目录中的文件描述节点 是如何构建的, 加上许多 GYP 变量来控制 节点的哪些部分已启用以及是否打开某些配置。

To change the build flags, you need to set the variables in the .gypi file of your project. The configure script in Node can generate some common configurations for you, for example running ./configure --shared will generate a config.gypi with variables instructing Node to be built as a shared library.

Electron does not use the configure script since it has its own build scripts. 节点配置在 common.gypi 文件 中定义了 Electron的根源代码目录。

In Electron, Node is being linked as a shared library by setting the GYP variable node_shared to true, so Node's build type will be changed from executable to shared_library, and the source code containing the Node's main entry point will not be compiled.

Since Electron uses the V8 library shipped with Chromium, the V8 library included in Node's source code is not used. This is done by setting both node_use_v8_platform and node_use_bundled_v8 to false.

Shared library or static library

When linking with Node, there are two options: you can either build Node as a static library and include it in the final executable, or you can build it as a shared library and ship it alongside the final executable.

In Electron, Node was built as a static library for a long time. This made the build simple, enabled the best compiler optimizations, and allowed Electron to be distributed without an extra node.dll file.

然而,Chrome切换到使用 BoringSSL 后改变了这种情况。 BoringSSL is a fork of OpenSSL that removes several unused APIs and changes many existing interfaces. 因为节点仍在使用 OpenSSL,编译器会产生无数的 链接错误,如果它们是相互冲突的符号连接在一起的话。 Because Node still uses OpenSSL, the compiler would generate numerous linking errors due to conflicting symbols if they were linked together.

Electron 无法在节点中使用 BoringSSL 或在 Chromium 中使用 OpenSSL 所以唯一的 选项是切换到构建节点作为共享库, 和 隐藏每个组件中的 BoringSSL 和 OpenSSL 符号

This change brought Electron some positive side effects. Before this change, you could not rename the executable file of Electron on Windows if you used native modules because the name of the executable was hard coded in the import library. After Node was built as a shared library, this limitation was gone because all native modules were linked to node.dll, whose name didn't need to be changed.

Supporting native modules

节点工作中的原生模块 ,定义节点加载的条目函数。 然后从节点中搜索V8和libuv 的符号。 This is a bit troublesome for embedders because by default the symbols of V8 and libuv are hidden when building Node as a library and native modules will fail to load because they cannot find the symbols.

So in order to make native modules work, the V8 and libuv symbols were exposed in Electron. For V8 this is done by forcing all symbols in Chromium's configuration file to be exposed. For libuv, it is achieved by setting the BUILDING_UV_SHARED=1 definition.

Starting Node in your app

After all the work of building and linking with Node, the final step is to run Node in your app.

Node doesn't provide many public APIs for embedding itself into other apps. 通常您只能调用 节点:start节点::Init 开始 新的节点实例。 However, if you are building a complex app based on Node, you have to use APIs like node::CreateEnvironment to precisely control every step.

In Electron, Node is started in two modes: the standalone mode that runs in the main process, which is similar to official Node binaries, and the embedded mode which inserts Node APIs into web pages. The details of this will be explained in a future post.

July 2016: New Apps and Meetups

· 阅读时间:约 2 分钟

We're starting a monthly roundup to highlight activity in the Electron community. Each roundup will feature things like new apps, upcoming meetups, tools, videos, etc.


This site is updated with new apps and meetups through pull requests from the community. 你可以查看库以获取新添加内容的通知,或如果你对_所有_更新不感兴趣,关注博客的RSS订阅

如果您已经制作了一个 Electron 应用程序或主办了一次会议,提交一个 拉取请求 添加到站点,它将被包括进下一次统计。

新应用

DemioA Webinar platform built for inbound sales and marketing
ElectorrentA remote client app for uTorrent server
PhoneGapThe open source framework that gets you building amazing mobile apps using web technology
WordMarkA lightweight blog publishing editor for Markdown writers
UbAuthApp to help developers create access tokens for Uber applications with OAuth 2.0
HyperTermHTML/JS/CSS terminal
MarpMarkdown presentation writer
Glyphr StudioA free, web based font designer, focusing on font design for hobbyists
BitCryptA simple file encryption application for Windows Encrypt your bits
TrymBeautiful small app for macOS to help you view, optimize and convert SVG icons
BookerText editor with the power of Markdown
PhonePresenterThe smartest presentation clicker
YoutThe new way to watch your playlists from YouTube on desktop

New Meetups

Electron Open Source Desktop FrameworkLondon, UK

Electron Internals: Message Loop Integration

· 阅读时间:约 4 分钟

This is the first post of a series that explains the internals of Electron. This post introduces how Node's event loop is integrated with Chromium in Electron.


node-gui for GTK+ bindings, and node-qt for QT bindings. 但其中没有一个在生产中工作,因为图形界面工具包有自己的消息 循环,而诺德则在自己的事件循环中使用 libuv , 并且主线程只能同时运行 个循环。 But none of them work in production because GUI toolkits have their own message loops while Node uses libuv for its own event loop, and the main thread can only run one loop at the same time. So the common trick to run GUI message loop in Node is to pump the message loop in a timer with very small interval, which makes GUI interface response slow and occupies lots of CPU resources.

During the development of Electron we met the same problem, though in a reversed way: we had to integrate Node's event loop into Chromium's message loop.

The main process and renderer process

Before we dive into the details of message loop integration, I'll first explain the multi-process architecture of Chromium.

Electron 有两种类型的进程:主进程和渲染器 进程(这实际上是非常简化的, 完整的视图请查看 多进程架构。 The main process is responsible for GUI work like creating windows, while the renderer process only deals with running and rendering web pages.

Electron allows using JavaScript to control both the main process and renderer process, which means we have to integrate Node into both processes.

Replacing Chromium's message loop with libuv

My first try was reimplementing Chromium's message loop with libuv.

It was easy for the renderer process, since its message loop only listened to file descriptors and timers, and I only needed to implement the interface with libuv.

However it was significantly more difficult for the main process. Each platform has its own kind of GUI message loops. macOS Chromium uses NSRunLoop, whereas Linux uses glib. I tried lots of hacks to extract the underlying file descriptors out of the native GUI message loops, and then fed them to libuv for iteration, but I still met edge cases that did not work.

So finally I added a timer to poll the GUI message loop in a small interval. As a result the process took a constant CPU usage, and certain operations had long delays.

Polling Node's event loop in a separate thread

As libuv matured, it was then possible to take another approach.

The concept of backend fd was introduced into libuv, which is a file descriptor (or handle) that libuv polls for its event loop. So by polling the backend fd it is possible to get notified when there is a new event in libuv.

So in Electron I created a separate thread to poll the backend fd, and since I was using the system calls for polling instead of libuv APIs, it was thread safe. And whenever there was a new event in libuv's event loop, a message would be posted to Chromium's message loop, and the events of libuv would then be processed in the main thread.

In this way I avoided patching Chromium and Node, and the same code was used in both the main and renderer processes.

The code

您可以在 electron/atom/common/ 目录下node_bindings 文件中找到消息循环集成的实现方式。 It can be easily reused for projects that want to integrate Node.

Update: Implementation moved to electron/shell/common/node_bindings.cc.

Electron Podcasts

· 阅读时间:约 1 分钟

Looking for an introduction to Electron? Two new podcasts have just been released that give a great overview of what it is, why it was built, and how it is being used.


Out now:

Hanselminutes: Creating cross-platform Electron apps

Is Electron "just Chrome in a frame" or is it so much more? Jessica sets Scott on the right path and explains exactly where the Electron platform fits into your development world.


JavaScript Air: Electron Apps

Electron is becoming more and more of a relevant and popular way of building multi-platform desktop apps with web technologies. Let's get a dive into this awesome tech and see how we can use it to enhance our own experience and our user's experience on the desktop.


If you're looking for an introduction to Electron, give the first a listen. The second goes into more detail about building apps with great tips from Nylas's Evan Morikawa.

We are currently working on two more podcasts that should come out next month, keep an eye on the @ElectronJS Twitter account for updates.

Electron 1.0

· 阅读时间:约 5 分钟

在过去的两年中,Electron帮助开发者使用HTML,CSS和JavaScript构建跨平台 桌面应用程序。 现在我们很高兴与我们的框架和创建它的社区分享一个重要的 里程碑。 Electron 1.0 的版本 现在可从 electrjs.org 获得。


Electron 1.0

Electron 1.0代表了API稳定性和成熟度的重要里程碑。 此版本允许您构建在 Windows、 Mac 和 Linux 上有真正原生运行体验的应用程序。 使用新文档、 新工具以及引导您完成 Electron API 的新应用程序,构建 Electron 应用程序比以往任何时候都容易。

如果您已经准备好构建您的第一个Electron应用程序,这里有一个 快速启动指南 来帮助您启动程序。

我们很期待看到您接下来使用Electron构建的项目。

Electron的道路

我们在两年多前启动了 Atom 时发布了Electron。 当时被称为Atom Shell的Electron,是我们在其上构建Atom的框架。 In those days, Atom was the driving force behind the features and functionalities that Electron provided as we pushed to get the initial Atom release out.

现在驾车的 Electron 是一个不断增长的开发者社区和公司构建 来自 email的所有东西, 聊天Git 应用SQL 分析工具, 种子客户端机器人

In these last two years we've seen both companies and open source projects choose Electron as the foundation for their apps. Just in the past year, Electron has been downloaded over 1.2 million times. 参加一些惊人的 Electron 应用程序的 的游览 ,如果它还不存在,请自己添加。

Electron 下载

Electron API 演示

随着 1.0 版本的发布,我们将发布一个新的应用以帮助您探索Electron API ,并详细了解如何让您的 Electron 应用程序感觉原生。 Electron API Demos 应用程序包含代码片段以帮助 你启动你的应用程序,并且提示如何有效地使用 Electron API。

Electron API 演示

Devtron

我们还添加了一个新的扩展程序,以帮助您调试Electron 应用程序。 Devtron 是一个开放源码扩展到 Chrome 开发者工具 旨在帮助您查看, 调试和疑难解答您的 Electron 应用。

Devtron

功能

  • Require graph ,以帮助你可视化主进程和渲染器进程中应用的内部和外部库依赖项
  • IPC monitor ,用于跟踪和显示应用中进程之间发送和接收的消息
  • 事件检查器 ,向您显示 在应用程序的核心 Electron API(如窗口、应用程序和进程)上注册的事件和侦听器
  • App Linter that checks your apps for common mistakes and missing functionality

Spectron

最后,我们正在发布一个新版本的 Spectron,Electron 应用程序的集成 测试框架。

Spectron

Spectron 3.0全面支持整个Electron API,使您可以 更快地编写测试,以验证应用程序在各种 场景和环境下的行为。 Spectron基于 ChromeDriverWebDriverIO 因此它还具有用于页面导航,用户 输入和JavaScript执行的完整API。

社区

Electron 1.0是数百名开发人员社区努力的结果。 在核心框架之外,已经有数百个库和工具 被发布,以使构建、包装和部署Electron应用程序更容易。

现在有一个新的 社区 页面列出了正在开发的许多超棒的 Electron 工具、应用、库和框架。 您也可以 查看 ElectronElectron Userland 组织来查看一些这些奇妙的项目。

Electron新手? 观看 Electron 1.0 简介视频:

Electron 0.37 有哪些新功能

· 阅读时间:约 4 分钟

Electron 0.37 was recently released and included a major upgrade from Chrome 47 to Chrome 49 and also several new core APIs. This latest release brings in all the new features shipped in Chrome 48 and Chrome 49. This includes CSS custom properties, increased ES6 support, KeyboardEvent improvements, Promise improvements, and many other new features now available in your Electron app.


What's New

CSS Custom Properties

If you've used preprocessed languages like Sass and Less, you're probably familiar with variables, which allow you to define reusable values for things like color schemes and layouts. Variables help keep your stylesheets DRY and more maintainable.

CSS custom properties are similar to preprocessed variables in that they are reusable, but they also have a unique quality that makes them even more powerful and flexible: they can be manipulated with JavaScript. This subtle but powerful feature allows for dynamic changes to visual interfaces while still benefitting from CSS's hardware acceleration, and reduced code duplication between your frontend code and stylesheets.

For more info on CSS custom properties, see the MDN article and the Google Chrome demo.

CSS Variables In Action

Let's walk through a simple variable example that can be tweaked live in your app.

:root {
--awesome-color: #a5ecfa;
}

body {
background-color: var(--awesome-color);
}

The variable value can be retrieved and changed directly in JavaScript:

// Get the variable value ' #A5ECFA'
let color = window
.getComputedStyle(document.body)
.getPropertyValue('--awesome-color');

// Set the variable value to 'orange'
document.body.style.setProperty('--awesome-color', 'orange');

The variable values can be also edited from the Styles section of the development tools for quick feedback and tweaks:

CSS properties in Styles tab

KeyboardEvent.code Property

Chrome 48 added the new code property available on KeyboardEvent events that will be the physical key pressed independent of the operating system keyboard layout.

This should make implementing custom keyboard shortcuts in your Electron app more accurate and consistent across machines and configurations.

window.addEventListener('keydown', function (event) {
console.log(`${event.code} was pressed.`);
});

Check out this example to see it in action.

Promise Rejection Events

Chrome 49 added two new window events that allow you to be notified when an rejected Promise goes unhandled.

window.addEventListener('unhandledrejection', function (event) {
console.log('A rejected promise was unhandled', event.promise, event.reason);
});

window.addEventListener('rejectionhandled', function (event) {
console.log('A rejected promise was handled', event.promise, event.reason);
});

Check out this example to see it in action.

ES2015 Updates in V8

The version of V8 now in Electron incorporates 91% of ES2015. Here are a few interesting additions you can use out of the box—without flags or pre-compilers:

Default parameters

function multiply(x, y = 1) {
return x * y;
}

multiply(5); // 5

解构赋值

Chrome 49 added destructuring assignment to make assigning variables and function parameters much easier.

This makes Electron requires cleaner and more compact to assign now:

Browser Process Requires
const { app, BrowserWindow, Menu } = require('electron');
Renderer Process Requires
const { dialog, Tray } = require('electron').remote;
Other Examples
// Destructuring an array and skipping the second element
const [first, , last] = findAll();

// Destructuring function parameters
function whois({ displayName: displayName, fullName: { firstName: name } }) {
console.log(`${displayName} is ${name}`);
}

let user = {
displayName: 'jdoe',
fullName: {
firstName: 'John',
lastName: 'Doe',
},
};
whois(user); // "jdoe is John"

// Destructuring an object
let { name, avatar } = getUser();

New Electron APIs

A few of the new Electron APIs are below, you can see each new API in the release notes for Electron releases.

show and hide events on BrowserWindow

These events are emitted when the window is either shown or hidden.

const { BrowserWindow } = require('electron');

let window = new BrowserWindow({ width: 500, height: 500 });
window.on('show', function () {
console.log('Window was shown');
});
window.on('hide', function () {
console.log('Window was hidden');
});

platform-theme-changed on app for OS X

This event is emitted when the system’s Dark Mode theme is toggled.

const { app } = require('electron');

app.on('platform-theme-changed', function () {
console.log(`Platform theme changed. In dark mode? ${app.isDarkMode()}`);
});

app.isDarkMode() for OS X

This method returns true if the system is in Dark Mode, and false otherwise.

scroll-touch-begin and scroll-touch-end events to BrowserWindow for OS X

These events are emitted when the scroll wheel event phase has begun or has ended.

const { BrowserWindow } = require('electron');

let window = new BrowserWindow({ width: 500, height: 500 });
window.on('scroll-touch-begin', function () {
console.log('Scroll touch started');
});
window.on('scroll-touch-end', function () {
console.log('Scroll touch ended');
});

Use V8 and Chromium Features in Electron

· 阅读时间:约 2 分钟

Building an Electron application means you only need to create one codebase and design for one browser, which is pretty handy. But because Electron stays up to date with Node.js and Chromium as they release, you also get to make use of the great features they ship with. In some cases this eliminates dependencies you might have previously needed to include in a web app.


There are many features and we'll cover some here as examples, but if you're interested in learning about all features you can keep an eye on the Google Chromium blog and Node.js changelogs. You can see what versions of Node.js, Chromium and V8 Electron is using at electronjs.org/#electron-versions.

ES6 Support through V8

Electron combines Chromium's rendering library with Node.js. The two share the same JavaScript engine, V8. Many ECMAScript 2015 (ES6) features are already built into V8 which means you can use them in your Electron application without any compilers.

Below are a few examples but you can also get classes (in strict mode), block scoping, promises, typed arrays and more. Check out this list for more information on ES6 features in V8.

Arrow Functions

findTime () => {
console.log(new Date())
}

String Interpolation

var octocat = 'Mona Lisa';
console.log(`The octocat's name is ${octocat}`);

New Target

Octocat() => {
if (!new.target) throw "Not new";
console.log("New Octocat");
}

// Throws
Octocat();
// Logs
new Octocat();

Array Includes

// Returns true
[1, 2].includes(2);

Rest Parameters

// Represent indefinite number of arguments as an array
(o, c, ...args) => {
console.log(args.length);
};

Chromium Features

Thanks to all the hard work Google and contributors put into Chromium, when you build Electron apps you can also use cool things like (but not limited to):

Follow along with the Google Chromium blog to learn about features as new versions ship and again, you can check the version of Chromium that Electron uses here.

What are you excited about?

Tweet to us @ElectronJS with your favorite features built into V8 or Chromium.

API Changes Coming in Electron 1.0

· 阅读时间:约 4 分钟

Since the beginning of Electron, starting way back when it used to be called Atom-Shell, we have been experimenting with providing a nice cross-platform JavaScript API for Chromium's content module and native GUI components. The APIs started very organically, and over time we have made several changes to improve the initial designs.


Now with Electron gearing up for a 1.0 release, we'd like to take the opportunity for change by addressing the last niggling API details. The changes described below are included in 0.35.x, with the old APIs reporting deprecation warnings so you can get up to date for the future 1.0 release. An Electron 1.0 won't be out for a few months so you have some time before these changes become breaking.

弃用警告

By default, warnings will show if you are using deprecated APIs. To turn them off you can set process.noDeprecation to true. To track the sources of deprecated API usages, you can set process.throwDeprecation to true to throw exceptions instead of printing warnings, or set process.traceDeprecation to true to print the traces of the deprecations.

使用内置模块的新方式

内置模块现在分成一个模块,而不是分成独立模块, 这样您就可以使用它们 而不会与其他模块 相冲突:

var app = require('electron').app;
var BrowserWindow = require('electron').BrowserWindow;

The old way of require('app') is still supported for backward compatibility, but you can also turn if off:

require('electron').hideInternalModules();
require('app'); // throws error.

An easier way to use the remote module

Because of the way using built-in modules has changed, we have made it easier to use main-process-side modules in renderer process. You can now just access remote's attributes to use them:

// New way.
var app = require('electron').remote.app;
var BrowserWindow = require('electron').remote.BrowserWindow;

Instead of using a long require chain:

// Old way.
var app = require('electron').remote.require('app');
var BrowserWindow = require('electron').remote.require('BrowserWindow');

Splitting the ipc module

The ipc module existed on both the main process and renderer process and the API was different on each side, which is quite confusing for new users. We have renamed the module to ipcMain in the main process, and ipcRenderer in the renderer process to avoid confusion:

// 在主进程中.
var ipcMain = require('electron').ipcMain;
// In renderer process.
var ipcRenderer = require('electron').ipcRenderer;

And for the ipcRenderer module, an extra event object has been added when receiving messages, to match how messages are handled in ipcMain modules:

ipcRenderer.on('message', function (event) {
console.log(event);
});

BrowserWindow 的标准化

The BrowserWindow options had different styles based on the options of other APIs, and were a bit hard to use in JavaScript because of the - in the names. They are now standardized to the traditional JavaScript names:

new BrowserWindow({ minWidth: 800, minHeight: 600 });

Following DOM's conventions for API names

The API names in Electron used to prefer camelCase for all API names, like Url to URL, but the DOM has its own conventions, and they prefer URL to Url, while using Id instead of ID. We have done the following API renames to match the DOM's styles:

  • Url 更名为 URL
  • Csp 更名为 CSP

You will notice lots of deprecations when using Electron v0.35.0 for your app because of these changes. An easy way to fix them is to replace all instances of Url with URL.

Changes to Tray's event names

The style of Tray event names was a bit different from other modules so a rename has been done to make it match the others.

  • clicked 更名为 click
  • double-clicked 更名为 double-click
  • right-clicked 更名为 right-click