前言

为了进一步提高开发工作效率,最近我们基于 electron 开发了一款媲美 uTools 的开源工具箱 rubick。该工具箱不仅仅开源,最重要的是可以使用 uTools 生态内所有开源插件!这将是巨大的能力,意味着 uTools 生态内所有插件可以无差异化使用到 rubick 中。

设计交互上为了更能提高用户的使用效率,我们又尝试去实现了 uTools 中非常优秀的一些设计,比如:超级面板。该功能可以通过鼠标快速唤起uTools 插件能力,而不用再打开应用。比如上传图片,只要我们安装了图床插件,那么当鼠标选择桌面上某张图片时,即可快速呼出上传图片的菜单选项,方便省事。接下来一起看看实现方式吧!

代码仓库

Rubick github

功能截图:

文件夹下长按右建

选择文件后长按右键

选择文字后长按右键

实现原理

获取选中文案

要实现改功能核心是要读取当前用户选中的文案或者文件,根据当前选择内容进行不同功能展示。但是核心有一个问题是如何来实现获取当前选中的内容。这个问题思考了很久很久,要想获取选中的文案,感觉唯一的办法是使用 ctrl + c 或者 command + c 来先复制到剪切板,再通过 electron clipboard 来获取当前剪切板内容。但是 utools 可不是通过先复制再长按这样的操作来实现的,而是直接选中文本或者文件长按后呼起超级面板。所以一定要在右击长按前获取到当前选中的内容。

如果要这么干,可能真的无解了,之前就因为这么想,才被无解了。正确的思路应该是先长按再获取选中的内容。别看只是掉了个个,但实现确实天壤之别:

  1. 先获取选中内容:这就要求我们必须监听原生系统选中事件,但是 electron 并没有提供能力,我们也无法监听系统选择事件。
  2. 先右击,后获取内容,这样的好处在于先右击可以通过监听鼠标右击事件,相比选择事件更加容易。

所以思路就有了,先监听长按右击事件:

// macos
const mouseEvents = require("osx-mouse"); const mouseTrack = mouseEvents();
// 按下去的 time
let down_time = 0; // 是否弹起
let isPress = false; // 监听右击
mouseTrack.on('right-down', () => {
isPress = true;
down_time = Date.now();
// 长按 500ms 后触发
setTimeout(async () => {
if (isPress) {
// 获取选中内容
const copyResult = await getSelectedText();
}, 500);
})
mouseTrack.on('right-up', () => {
isPress = false;
});

接下来一步就是要去实现获取选中内容,要获取选中内容有个比较骚的操作,就是:

  1. 通过 clipboard 先获取当前剪切板内容,并存下 A
  2. 通过 robot.js 来调用系统 command + c 或者 ctrl + c
  3. 再通过 clipboard 先获取当前剪切板内容,并存下 B
  4. 再将 A 写到剪切板中,返回 B

先存剪切板内容的目的在于我们是偷偷帮用户执行了复制动作,当读取完用户选择内容后,需要回复用户之前的剪切板内容。接下来看一下简单的实现:

const getSelected = () => {
return new Promise((resolve) => {
// 缓存之前的文案
const lastText = clipboard.readText('clipboard'); const platform = process.platform; // 执行复制动作
if (platform === 'darwin') {
robot.keyTap('c', 'command');
} else {
robot.keyTap('c', 'control');
} setTimeout(() => {
// 读取剪切板内容
const text = clipboard.readText('clipboard') || ''
const fileUrl = clipboard.read('public.file-url'); // 恢复剪切板内容
clipboard.writeText(lastText); resolve({
text,
fileUrl
})
}, 300);
})
}

通知超级面板窗口当前选中内容

当获取到了选中内容后,接下来就是需要创建超级面板的 BrowserWindow:

const { BrowserWindow, ipcMain, app } = require("electron");

module.exports = () => {
let win; let init = (mainWindow) => {
if (win === null || win === undefined) {
createWindow();
}
}; let createWindow = () => {
win = new BrowserWindow({
frame: false,
autoHideMenuBar: true,
width: 250,
height: 50,
show: false,
alwaysOnTop: true,
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
backgroundThrottling: false,
nodeIntegration: true,
devTools: false,
},
});
win.loadURL(`file://${__static}/plugins/superPanel/index.html`);
win.once('ready-to-show', () => win.show());
win.on("closed", () => {
win = undefined;
});
}; let getWindow = () => win; return {
init: init,
getWindow: getWindow,
};
};

然后再通知 superPanel 进行内容展示:

 win.webContents.send('trigger-super-panel', {
...copyResult,
optionPlugin: optionPlugin.plugins,
});

超级面板点击操作

接下来要实现超级面板点击操作,这块也是比较简单的了,直接上代码好了:

1. 打开 Terminal

const { spawn } = require ('child_process');

spawn('open', [ '-a', 'Terminal', fileUrl ]);

2. 新建文件

remote.dialog.showSaveDialog({
title: "请选择要保存的文件名",
buttonLabel: "保存",
defaultPath: fileUrl.replace('file://', ''),
showsTagField: false,
nameFieldLabel: '',
}).then(result => {
fs.writeFileSync(result.filePath, '');
});

3. 复制路径

clipboard.writeText(fileUrl.replace('file://', ''))

最后

本篇主要介绍如何实现一个类似于 utools 的超级面板功能,当然这远远不是 utools 的全部,下期我们再继续介绍如何实现 utools 其他能力。欢迎大家前往体验 Rubick 有问题可以随时提 issue 我们会及时反馈。

另外,如果觉得设计实现思路对你有用,也欢迎给个 Star:https://github.com/clouDr-f2e/rubick

最新文章

  1. 每天一个linux命令(25):linux文件属性详解
  2. C++ 小工具一键解决SVN Clean Up 失败的问题
  3. velocity +mybatis+ springMvc构建邮件服务器知识总结
  4. 在WebApi中实现Cors访问
  5. JQuery直接调用asp.net后台WebMethod方法
  6. C++Primer 第十九章
  7. Ubuntu 下 Neo4j单机安装和集群环境安装
  8. 最小化安装Centos7后的部署(个人)
  9. Android开发系列之学习路线图
  10. docker 指定容器名字
  11. Android自己定义控件系列五:自己定义绚丽水波纹效果
  12. Nginx “邪恶” rewrite
  13. laravel 中使用ajax和vue总结
  14. 201521123106 《Java程序设计》第11周学习总结
  15. mysql命令行的导入导出sql,txt,excel(都在linux或windows命令行操作)(转自筑梦悠然)
  16. 记使用aliyun-log-logback-appender 报错no applicable action for [encoder], current ElementPath is [[configuration][appender][encoder]]
  17. 二、IPC机制
  18. SEO : 建站注意
  19. English trip -- VC(情景课)2 C Where's my pencli?
  20. Effective C++ 38-42

热门文章

  1. [Python] 网络
  2. 在写脚本时,在一开始(Shebang 之后)就加上这一句,或者它的缩略版: set -xeuo pipefail
  3. Zabbix 监控介绍
  4. 032.Python魔术方法__new__和单态模式
  5. Java中生成唯一标识符的方法(Day_48)
  6. Docker系列——Grafana+Prometheus+Node-exporter服务器告警中心(二)
  7. LogBack 日志等级设置无效,原因竟然是因为这个?!
  8. 痞子衡嵌入式:改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常
  9. 在Docker中安装MongoDB
  10. 使用ONNX将模型转移至Caffe2和移动端