fix: 改进用户体验

定期执行自动操作保持mini窗口活跃防止长时间无操作后唤醒卡顿;
主窗口隐藏时允许mini窗口出现在Windows切换视图中(取消skipTaskBar标识)以快速切换;
This commit is contained in:
2020-06-13 14:11:06 +08:00
parent a7a76dfde2
commit dc054e40db
5 changed files with 107 additions and 61 deletions
+10 -3
View File
@@ -25,15 +25,18 @@ Google Translate网页版的封装,通过[Electron](https://www.electronjs.org
* 允许配置默认主窗口尺寸;
* Mini窗口自动获取输入焦点;
* 支持开机启动;
* 允许Mini窗口隐藏时保持活跃以快速响应;
<br/>
* 不支持离线使用;
* 不支持取词划词;
* 未测试Windows之外的平台;
对于Mini窗口自动获取输入焦点,由于是通过`dom-ready`事件注入JavaScript实现,因此在`dom-ready`前Mini窗口无法自动获取焦点。而这需要大概十几秒,因为`translate.google.cn`尝试从`google.com`获取一些资源,而访问google.com会等待超时后失败(国内环境),但这些资源并不是完成翻译功能所必须的,因此并不影响翻译功能的使用。但未完成的非ajax请求会让`dom-ready`事件迟迟不能触发,导致依赖此事件注入JavaScript的代码直到请求超时后才执行。
对于Mini窗口自动获取输入焦点和自动清除输入,由于是通过`dom-ready`事件注入JavaScript实现,因此在`dom-ready`后才能生效。而这需要大概十几秒,因为`translate.google.cn`尝试从`google.com`获取一些资源,而访问google.com会等待超时后失败(国内环境),但这些资源并不是完成翻译功能所必须的,因此并不影响翻译功能的使用。但未完成的非ajax请求会让`dom-ready`事件迟迟不能触发,导致依赖此事件注入JavaScript的代码直到请求超时后才执行。
但这只在程序启动时发生,个人认为勉强能够忍受,就没有再折腾。如果有必要,或许可以通过`ServiceWorker`拦截?
另外,在此期间,对于0.2版本可能会出现输入内容离奇消失的情况(每次运行周期最多出现一次)。这是因为程序在Mini窗口隐藏时自动清除输入,但由于注入的JS代码需要`dom-ready`事件后才执行,所以清除输入的代码可能会在不合适的时机被执行。但至多只可能发生一次。
对于封装的url,本程序仅对translate.google.cn做了样式微调,如果换成其它的url,可能需要自行调整样式。配置项中允许更改url参数来加载其他网页。如果需要,还可以通过提供额外CSS来调整样式,它们将被注入页面,见[配置项](##配置项)。
注意,你可能需要为你的样式加上!important标记以覆盖更高优先级的原始样式,因为CSS以嵌入式CSS的方式插入。
@@ -72,14 +75,18 @@ Mini窗口的大小是固定的,想要修改只能修改代码。为了在更
| extraCssForMini | string | "" | 额外插入Mini窗口的的CSS
| minimizeToTrayWhenClose | boolean | true | 关闭主窗口时最小化到托盘
| minimizeToTrayWenStart | boolean | true | 启动时自动隐藏主窗口
| keepActiveInterval | number | 60000 | 自动操作Mini窗口以使其保持活跃的周期,单位ms,最低1000,为0禁止
## 使用截图
<img src="./blob/main.png" alt="主窗口" />
主窗口
<img src="./blob/main.png" alt="主窗口" />
<img src="./blob/main-input.png" alt="主窗口-有内容" />
<img src="./blob/mini.png" alt="迷你窗口-有内容" />
Mini窗口
<img src="./blob/mini.png" alt="迷你窗口-有内容" />
<img src="./blob/mini-input.png" alt="迷你窗口-有内容" />
+1
View File
@@ -14,6 +14,7 @@ const config = {
extraCssForMini: '',
minimizeToTrayWhenClose: true,
minimizeToTrayWenStart: true,
keepActiveInterval: 60000
};
try {
+9 -32
View File
@@ -1,7 +1,7 @@
const path = require('path');
const { app, globalShortcut, BrowserWindow, Menu, Tray } = require('electron');
const createMiniWindow = require('./mini');
const createMainWindow = require('./main');
const { createMiniWindow, toggleMiniWindowVisibility } = require('./mini');
const { createMainWindow, toggleMainWindowVisibility } = require('./main');
const getMenu = require('./menu');
const { config, flushConfig } = require('./config');
const toast = require('./notify');
@@ -36,12 +36,14 @@ app.on('ready', () => {
mainWindow.on('hide', () => {
if (miniWindow) {
miniWindow.setParentWindow(mainWindow);
miniWindow.setSkipTaskbar(false);
}
});
//主窗口显示时如果mini窗口的父窗口是主窗口,mini窗口获得焦点时父窗口也会弹出来。。emm又没找到其他靠谱的方法
mainWindow.on('show', () => {
if (miniWindow) {
miniWindow.setParentWindow(null);
miniWindow.setSkipTaskbar(true);
}
});
if (!config.minimizeToTrayWenStart) {
@@ -59,10 +61,7 @@ app.on('ready', () => {
});
function registerMiniWindow() {
// if (!config.miniWindowEnabled) {
// return;
// }
miniWindow = createMiniWindow();
miniWindow = createMiniWindow();
miniWindow.setParentWindow(mainWindow);
const miniMenu = getMenu(mainWindow, miniWindow, miniWindow);
miniWindow.setMenu(miniMenu);
@@ -74,8 +73,8 @@ function registerMiniWindow() {
function setupTray() {
tray = new Tray(path.resolve(__dirname, 'assets/icon.ico'));
const contextMenu = Menu.buildFromTemplate([
{ label: "显示主界面", click: () => toggleMainWindowVisibility() },
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility() },
{ label: "显示主界面", click: () => toggleMainWindowVisibility(mainWindow) },
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility(miniWindow) },
{
type: 'checkbox',
label: "开机启动",
@@ -91,7 +90,7 @@ function setupTray() {
{ label: "退出", click: () => app.exit() }
]);
tray.setToolTip("Google Translate Desktop");
tray.on('click', () => toggleMainWindowVisibility());
tray.on('click', () => toggleMainWindowVisibility(mainWindow));
tray.setContextMenu(contextMenu);
}
@@ -124,7 +123,7 @@ function toggleMiniShortcut(initial) {
let enabled = initial ? config.miniWindowEnabled : !config.miniWindowEnabled;
let success = true;
if (enabled) {
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility())) {
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility(miniWindow))) {
toast.show("快捷键注册失败,请检查设置的快捷键是否被其他软件占用。", "错误", {
type: 'error'
});
@@ -142,25 +141,3 @@ function toggleMiniShortcut(initial) {
}
return enabled;
}
function toggleMiniWindowVisibility() {
if (!miniWindow) { return; }
if (miniWindow.isVisible()) {
miniWindow.hide();
}
else {
miniWindow.show();
miniWindow.focus();
}
}
function toggleMainWindowVisibility() {
if (!mainWindow) { return; }
if (mainWindow.isVisible()) {
mainWindow.hide();
}
else {
mainWindow.show();
mainWindow.focus();
}
}
+16 -2
View File
@@ -1,6 +1,5 @@
const path = require('path');
const { BrowserWindow } = require('electron');
const getMenu = require('./menu');
const { config } = require('./config');
function createMainWindow() {
@@ -36,4 +35,19 @@ function createMainWindow() {
return win;
}
module.exports = createMainWindow;
function toggleMainWindowVisibility(window) {
if (!window) { return; }
if (window.isVisible()) {
window.hide();
}
else {
window.show();
window.focus();
}
}
module.exports = {
createMainWindow,
toggleMainWindowVisibility
}
+71 -24
View File
@@ -1,8 +1,14 @@
const path = require('path');
const { BrowserWindow } = require('electron');
const getMenu = require('./menu');
const { config } = require('./config');
const miniControl = {
/** 迷你窗口是否处于可视区域 */
isMiniWindowVisible: true,
previousPos: null
}
function createMiniWindow () {
let miniWindow = new BrowserWindow({
width: 500,
@@ -22,7 +28,8 @@ function createMiniWindow () {
maximizable: true,
alwaysOnTop: true,
fullscreenable: false,
skipTaskbar: true,
skipTaskbar: false,
});
miniWindow.on('focus', () => {
@@ -33,28 +40,11 @@ function createMiniWindow () {
});
miniWindow.on('close', ev => {
ev.preventDefault();
miniWindow.hide();
toggleMiniWindowVisibility(miniWindow);
});
miniWindow.loadURL(config.url);
miniWindow.webContents.on('dom-ready', () => {
miniWindow.webContents.executeJavaScript(`
setTimeout(() => {
console.log('throw');
throw('crashed');
}, 10000);
var source = document.getElementById('source');
if (source) {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
source.value = '';
}
else {
source.focus();
}
});
}
`);
miniWindow.webContents.insertCSS(`
/* 隐藏通知,文本/文件切换栏 */
.container .input-button-container,
@@ -62,8 +52,9 @@ function createMiniWindow () {
display: none !important;
}
/* mini窗口隐藏header和历史记录按钮和拼音 */
.tlid-source-transliteration-container.source-transliteration-container,
header.gb_sa.gb_2a.gb_5e.gb_ta, .gp-footer {
header,
.gp-footer,
.tlid-source-transliteration-container.source-transliteration-container {
display: none !important;
}
/* 防止高度较小时出现双滚动条 */
@@ -103,8 +94,64 @@ function createMiniWindow () {
miniWindow.insertCSS(config.extraCssForMini);
}
});
miniControl.previousPos = miniWindow.getPosition();
toggleMiniWindowVisibility(miniWindow);
miniWindow.show();
let interval = config.keepActiveInterval | 0;
if (miniWindow && interval !== 0) {
interval = Math.max(1000, interval);
setInterval(() => {
if (!miniControl.isMiniWindowVisible) {
miniWindow.webContents.executeJavaScript(`
var source = document.getElementById('source');
if (source) {
source.value = source.value;
}
`);
}
}, interval);
}
return miniWindow;
}
module.exports = createMiniWindow;
function toggleMiniWindowVisibility(window) {
if (!window) { return; }
miniControl.isMiniWindowVisible = !miniControl.isMiniWindowVisible;
if (miniControl.isMiniWindowVisible) {
if (miniControl.previousPos) {
window.setPosition(...miniControl.previousPos);
}
else {
window.show();
}
focusInput(window);
window.focus();
}
else {
miniControl.previousPos = window.getPosition();
window.setPosition(-1000, -1000, false);
clearInput(window);
}
}
function clearInput(window) {
window.webContents.executeJavaScript(`
var source = document.getElementById('source');
if (source) {
source.value = '';
}
`);
}
function focusInput (window) {
window.webContents.executeJavaScript(`
var source = document.getElementById('source');
if (source) {
source.focus();
}
`);
}
module.exports = {
createMiniWindow,
toggleMiniWindowVisibility
}