fix: 改进用户体验
定期执行自动操作保持mini窗口活跃防止长时间无操作后唤醒卡顿; 主窗口隐藏时允许mini窗口出现在Windows切换视图中(取消skipTaskBar标识)以快速切换;
This commit is contained in:
@@ -25,15 +25,18 @@ Google Translate网页版的封装,通过[Electron](https://www.electronjs.org
|
|||||||
* 允许配置默认主窗口尺寸;
|
* 允许配置默认主窗口尺寸;
|
||||||
* Mini窗口自动获取输入焦点;
|
* Mini窗口自动获取输入焦点;
|
||||||
* 支持开机启动;
|
* 支持开机启动;
|
||||||
|
* 允许Mini窗口隐藏时保持活跃以快速响应;
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
* 不支持离线使用;
|
* 不支持离线使用;
|
||||||
* 不支持取词划词;
|
* 不支持取词划词;
|
||||||
* 未测试Windows之外的平台;
|
* 未测试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`拦截?
|
但这只在程序启动时发生,个人认为勉强能够忍受,就没有再折腾。如果有必要,或许可以通过`ServiceWorker`拦截?
|
||||||
|
|
||||||
|
另外,在此期间,对于0.2版本可能会出现输入内容离奇消失的情况(每次运行周期最多出现一次)。这是因为程序在Mini窗口隐藏时自动清除输入,但由于注入的JS代码需要`dom-ready`事件后才执行,所以清除输入的代码可能会在不合适的时机被执行。但至多只可能发生一次。
|
||||||
|
|
||||||
对于封装的url,本程序仅对translate.google.cn做了样式微调,如果换成其它的url,可能需要自行调整样式。配置项中允许更改url参数来加载其他网页。如果需要,还可以通过提供额外CSS来调整样式,它们将被注入页面,见[配置项](##配置项)。
|
对于封装的url,本程序仅对translate.google.cn做了样式微调,如果换成其它的url,可能需要自行调整样式。配置项中允许更改url参数来加载其他网页。如果需要,还可以通过提供额外CSS来调整样式,它们将被注入页面,见[配置项](##配置项)。
|
||||||
注意,你可能需要为你的样式加上!important标记以覆盖更高优先级的原始样式,因为CSS以嵌入式CSS的方式插入。
|
注意,你可能需要为你的样式加上!important标记以覆盖更高优先级的原始样式,因为CSS以嵌入式CSS的方式插入。
|
||||||
|
|
||||||
@@ -72,14 +75,18 @@ Mini窗口的大小是固定的,想要修改只能修改代码。为了在更
|
|||||||
| extraCssForMini | string | "" | 额外插入Mini窗口的的CSS
|
| extraCssForMini | string | "" | 额外插入Mini窗口的的CSS
|
||||||
| minimizeToTrayWhenClose | boolean | true | 关闭主窗口时最小化到托盘
|
| minimizeToTrayWhenClose | boolean | true | 关闭主窗口时最小化到托盘
|
||||||
| minimizeToTrayWenStart | 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/main-input.png" alt="主窗口-有内容" />
|
||||||
|
|
||||||
<img src="./blob/mini.png" alt="迷你窗口-有内容" />
|
|
||||||
|
|
||||||
|
Mini窗口
|
||||||
|
|
||||||
|
<img src="./blob/mini.png" alt="迷你窗口-有内容" />
|
||||||
<img src="./blob/mini-input.png" alt="迷你窗口-有内容" />
|
<img src="./blob/mini-input.png" alt="迷你窗口-有内容" />
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const config = {
|
|||||||
extraCssForMini: '',
|
extraCssForMini: '',
|
||||||
minimizeToTrayWhenClose: true,
|
minimizeToTrayWhenClose: true,
|
||||||
minimizeToTrayWenStart: true,
|
minimizeToTrayWenStart: true,
|
||||||
|
keepActiveInterval: 60000
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
+8
-31
@@ -1,7 +1,7 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { app, globalShortcut, BrowserWindow, Menu, Tray } = require('electron');
|
const { app, globalShortcut, BrowserWindow, Menu, Tray } = require('electron');
|
||||||
const createMiniWindow = require('./mini');
|
const { createMiniWindow, toggleMiniWindowVisibility } = require('./mini');
|
||||||
const createMainWindow = require('./main');
|
const { createMainWindow, toggleMainWindowVisibility } = require('./main');
|
||||||
const getMenu = require('./menu');
|
const getMenu = require('./menu');
|
||||||
const { config, flushConfig } = require('./config');
|
const { config, flushConfig } = require('./config');
|
||||||
const toast = require('./notify');
|
const toast = require('./notify');
|
||||||
@@ -36,12 +36,14 @@ app.on('ready', () => {
|
|||||||
mainWindow.on('hide', () => {
|
mainWindow.on('hide', () => {
|
||||||
if (miniWindow) {
|
if (miniWindow) {
|
||||||
miniWindow.setParentWindow(mainWindow);
|
miniWindow.setParentWindow(mainWindow);
|
||||||
|
miniWindow.setSkipTaskbar(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//主窗口显示时如果mini窗口的父窗口是主窗口,mini窗口获得焦点时父窗口也会弹出来。。emm又没找到其他靠谱的方法
|
//主窗口显示时如果mini窗口的父窗口是主窗口,mini窗口获得焦点时父窗口也会弹出来。。emm又没找到其他靠谱的方法
|
||||||
mainWindow.on('show', () => {
|
mainWindow.on('show', () => {
|
||||||
if (miniWindow) {
|
if (miniWindow) {
|
||||||
miniWindow.setParentWindow(null);
|
miniWindow.setParentWindow(null);
|
||||||
|
miniWindow.setSkipTaskbar(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!config.minimizeToTrayWenStart) {
|
if (!config.minimizeToTrayWenStart) {
|
||||||
@@ -59,9 +61,6 @@ app.on('ready', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function registerMiniWindow() {
|
function registerMiniWindow() {
|
||||||
// if (!config.miniWindowEnabled) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
miniWindow = createMiniWindow();
|
miniWindow = createMiniWindow();
|
||||||
miniWindow.setParentWindow(mainWindow);
|
miniWindow.setParentWindow(mainWindow);
|
||||||
const miniMenu = getMenu(mainWindow, miniWindow, miniWindow);
|
const miniMenu = getMenu(mainWindow, miniWindow, miniWindow);
|
||||||
@@ -74,8 +73,8 @@ function registerMiniWindow() {
|
|||||||
function setupTray() {
|
function setupTray() {
|
||||||
tray = new Tray(path.resolve(__dirname, 'assets/icon.ico'));
|
tray = new Tray(path.resolve(__dirname, 'assets/icon.ico'));
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
{ label: "显示主界面", click: () => toggleMainWindowVisibility() },
|
{ label: "显示主界面", click: () => toggleMainWindowVisibility(mainWindow) },
|
||||||
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility() },
|
{ label: "显示Mini窗口", click: () => toggleMiniWindowVisibility(miniWindow) },
|
||||||
{
|
{
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
label: "开机启动",
|
label: "开机启动",
|
||||||
@@ -91,7 +90,7 @@ function setupTray() {
|
|||||||
{ label: "退出", click: () => app.exit() }
|
{ label: "退出", click: () => app.exit() }
|
||||||
]);
|
]);
|
||||||
tray.setToolTip("Google Translate Desktop");
|
tray.setToolTip("Google Translate Desktop");
|
||||||
tray.on('click', () => toggleMainWindowVisibility());
|
tray.on('click', () => toggleMainWindowVisibility(mainWindow));
|
||||||
tray.setContextMenu(contextMenu);
|
tray.setContextMenu(contextMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@ function toggleMiniShortcut(initial) {
|
|||||||
let enabled = initial ? config.miniWindowEnabled : !config.miniWindowEnabled;
|
let enabled = initial ? config.miniWindowEnabled : !config.miniWindowEnabled;
|
||||||
let success = true;
|
let success = true;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility())) {
|
if (!globalShortcut.register(config.miniShortcut, () => toggleMiniWindowVisibility(miniWindow))) {
|
||||||
toast.show("快捷键注册失败,请检查设置的快捷键是否被其他软件占用。", "错误", {
|
toast.show("快捷键注册失败,请检查设置的快捷键是否被其他软件占用。", "错误", {
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -142,25 +141,3 @@ function toggleMiniShortcut(initial) {
|
|||||||
}
|
}
|
||||||
return enabled;
|
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
@@ -1,6 +1,5 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { BrowserWindow } = require('electron');
|
const { BrowserWindow } = require('electron');
|
||||||
const getMenu = require('./menu');
|
|
||||||
const { config } = require('./config');
|
const { config } = require('./config');
|
||||||
|
|
||||||
function createMainWindow() {
|
function createMainWindow() {
|
||||||
@@ -36,4 +35,19 @@ function createMainWindow() {
|
|||||||
return win;
|
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
@@ -1,8 +1,14 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { BrowserWindow } = require('electron');
|
const { BrowserWindow } = require('electron');
|
||||||
const getMenu = require('./menu');
|
|
||||||
const { config } = require('./config');
|
const { config } = require('./config');
|
||||||
|
|
||||||
|
const miniControl = {
|
||||||
|
/** 迷你窗口是否处于可视区域 */
|
||||||
|
isMiniWindowVisible: true,
|
||||||
|
previousPos: null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function createMiniWindow () {
|
function createMiniWindow () {
|
||||||
let miniWindow = new BrowserWindow({
|
let miniWindow = new BrowserWindow({
|
||||||
width: 500,
|
width: 500,
|
||||||
@@ -22,7 +28,8 @@ function createMiniWindow () {
|
|||||||
maximizable: true,
|
maximizable: true,
|
||||||
alwaysOnTop: true,
|
alwaysOnTop: true,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
skipTaskbar: true,
|
skipTaskbar: false,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
miniWindow.on('focus', () => {
|
miniWindow.on('focus', () => {
|
||||||
@@ -33,28 +40,11 @@ function createMiniWindow () {
|
|||||||
});
|
});
|
||||||
miniWindow.on('close', ev => {
|
miniWindow.on('close', ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
miniWindow.hide();
|
toggleMiniWindowVisibility(miniWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
miniWindow.loadURL(config.url);
|
miniWindow.loadURL(config.url);
|
||||||
miniWindow.webContents.on('dom-ready', () => {
|
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(`
|
miniWindow.webContents.insertCSS(`
|
||||||
/* 隐藏通知,文本/文件切换栏 */
|
/* 隐藏通知,文本/文件切换栏 */
|
||||||
.container .input-button-container,
|
.container .input-button-container,
|
||||||
@@ -62,8 +52,9 @@ function createMiniWindow () {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
/* mini窗口隐藏header和历史记录按钮和拼音 */
|
/* mini窗口隐藏header和历史记录按钮和拼音 */
|
||||||
.tlid-source-transliteration-container.source-transliteration-container,
|
header,
|
||||||
header.gb_sa.gb_2a.gb_5e.gb_ta, .gp-footer {
|
.gp-footer,
|
||||||
|
.tlid-source-transliteration-container.source-transliteration-container {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
/* 防止高度较小时出现双滚动条 */
|
/* 防止高度较小时出现双滚动条 */
|
||||||
@@ -103,8 +94,64 @@ function createMiniWindow () {
|
|||||||
miniWindow.insertCSS(config.extraCssForMini);
|
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;
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user