大型情感类技术连续剧-徒手撸一个 uTools(二)
前言
上篇手把手教你实现一个支持插件化的 uTools 工具箱我们介绍过了如何通过 electron
实现 utools
的插件功能体系,并按照 utools
的交互和设计做出了一套可以支持插件化的桌面端工具箱 Rubick
。
本篇将继续为大家介绍如何再基于 electron
实现 utools
的搜索能力。
搜索能力实现
utools
搜索核心分为系统命令、插件命令、系统app功能搜索等几大类,下面我们来一一实现这3类功能的检索能力。
由于这 3 类搜索搜索出来的内容点击触发的交互不一样,所以我们设计了一个枚举类型来标记这三种检索内容,用于点击后触发不同的行为。
const SEARCH_TYPE = {
DEV: 'dev', // 测试插件
PROD: 'prod', // 已安装的插件
SYSTEM: 'system', // 系统插件
APP: 'app' // 应用 app
}
开发者插件
开发者插件分为已安装
和本地开发
2 种类型,分别根据 SEARCH_TYPE.PROD
和 SEARCH_TYPE.DEV
来进行区分。
搜索内容的基础数据结构如下:
const item = {
name: '搜索的title',
icon: '插件的icon',
desc: '插件的描述信息',
type: 'SEARCH_TYPE 对应的插件类型',
click: '点击事件'
}
拿一个具体的搜索插件举例:
const item = {
// 搜索插件功能对应的 cmd
name: cmd,
// 插件icon 地址
icon: plugin.sourceFile ? 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`) : plugin.logo,
// 功能描述
desc: fe.explain,
// 类型
type: plugin.type,
// 点击后的动作
click: (router) => {
actions.openPlugin({commit}, {cmd, plugin, feature: fe, router});
}
}
整体来看已安装插件和本地插件交互展示上唯一需要区分的是本地插件需要打上一个 tag
用于标记,这样才不至于混淆线上插件和本地插件:
<a-tag v-show="item.type === 'dev'">开发者</a-tag>
<a-tag v-show="item.type === 'system'">系统</a-tag>
我们来看一下完成后的效果:
会带有开发者标记。
接下来就是实现openPlugin
点击效果,点击核心能力是需要对 plugin
进行 webview 渲染。上篇问诊已经介绍过了如何实现这个 webview
这里就不在赘述,说一下点击逻辑:
openPlugin() {
commit('commonUpdate', {
// 点击后设置标签tag为搜索词
selected: {
key: 'plugin-container',
name: cmd,
icon: 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`),
},
// 清空搜索内容
searchValue: '',
// 展示 plugin webview
showMain: true
});
// 计算webview内容高度
ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight(),
});
}
再说一下点击后触发的逻辑步骤:
- 设置左上角标签内容为搜索关键词,并设置右上角 icon
- 清空搜索内容
- 打开
webview
加载插件,并动态计算插件高度
最后效果如下:
系统插件
系统插件是 utools
内置的,所以我们也需要将系统插件内置到 Rubick
中,这里我拿实现一个取色器来举例,去实现一个系统插件。首先先定义好系统插件的数据结构:
const SYSTEM_PLUFINS = [
{
"pluginName": "屏幕颜色拾取",
"logo": "https://alicdn.com/img/6a1b4b8a17da45d680ea30b53a91aca8.png",
"features": [
{
"code": "pick",
"explain": "rubick 帮助文档",
"cmds": [ "取色", "拾色", 'Pick color' ]
},
],
"tag": 'rubick-color',
}
]
字段说明:
- pluginName:系统插件展示的名称
- logo: 系统插件展示的logo
- features: 系统插件的功能列表
- feature.code: 系统插件执行的code码
- tag: 系统插件唯一标记
系统插件的交互展示和开发者插件本无太大的差异,核心较大的差异在于点击后的功能和开发者插件不太一样,我们来看看系统插件的点击交互逻辑:
opnPlugin() {
// 如果点击的是系统插件
if (plugin.type === 'system') {
// 调用系统函数
systemMethod[plugin.tag][feature.code]();
// 清空选择
commit('commonUpdate', {
selected: null,
showMain: false,
options: [],
});
// 设置高度为初始高度
ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight([]),
});
// 跳转到首页
router.push({
path: '/home',
});
}
}
所以对系统插件来说,由于系统插件本身并无 webview
所以不需要打开 webview
来承载插件,而是调用系统函数,比如 color-pick
调用的对应系统函数如下:
export default {
'rubick-color': {
pick() {
ipcRenderer.send('start-picker')
}
},
}
向 main
进程发送取色能力。如何取色将在后面章节介绍。实现后的交互如下:
系统app功能搜索
针对于 macos
用户,所安装的系统 App 都放在了 /System/Applications
和 /Applications
下,所以要实现 app 搜索,就是需要对 /System/Applications
和 /Applications
目录下的 app 进行检索。但有的时候除了 app 需要搜索,一些系统功能也需要搜索,比如偏好设置之类的。偏好设置一般存方的路径在 /System/Library/PreferencePanes
中。
接下来第一步需要做的是检束所有 app 和 PreferencePanes:
const APP_FINDER_PATH = [
'/System/Applications',
'/Applications',
'/System/Library/PreferencePanes',
];
APP_FINDER_PATH.forEach((searchPath) => {
// 搜索对应目录
fs.readdir(searchPath, (err, files) => {
// 查询所有 app 和 PreferencePanes
try {
for (let i = 0; i < files.length; i++) {
const appName = files[i];
const extname = path.extname(appName);
const appSubStr = appName.split(extname)[0];
if ((extname === '.app' || extname === '.prefPane') >= 0 ) {
// 查找 应用程序的 icon
try {
const path1 = path.join(searchPath, `${appName}/Contents/Resources/App.icns`);
const path2 = path.join(searchPath, `${appName}/Contents/Resources/AppIcon.icns`);
const path3 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr}.icns`);
const path4 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr.replace(' ', '')}.icns`);
let iconPath = path1;
if (fs.existsSync(path1)) {
iconPath = path1;
} else if (fs.existsSync(path2)) {
iconPath = path2;
} else if (fs.existsSync(path3)) {
iconPath = path3;
} else if (fs.existsSync(path4)) {
iconPath = path4;
} else {
// 性能最低的方式
const resourceList = fs.readdirSync(path.join(searchPath, `${appName}/Contents/Resources`));
const iconName = resourceList.filter(file => path.extname(file) === '.icns')[0];
iconPath = path.join(searchPath, `${appName}/Contents/Resources/${iconName}`);
}
// 创建图片
nativeImage.createThumbnailFromPath(iconPath, {width: 64, height: 64}).then(img => {
// 创建搜索项
fileLists.push({
name: appSubStr,
value: 'plugin',
icon: img.toDataURL(),
desc: path.join(searchPath, appName),
type: 'app',
action: `open ${path.join(searchPath, appName).replace(' ', '\\ ')}`
})
})
} catch (e) {
}
}
}
} catch (e) {
console.log(e);
}
});
});
代码看的有点多,其实很简单,主要也是几步走:
- 根据定义好的路径查找所有 app 和 PreferencePanes
- 应为下拉选项需要展示插件的 icon 所以对于 app 和 PreferencePanes 需要查找 icns
- 根据默认规则查找 icns 如果找不到再用性能较低的方式模糊匹配
- 检索成功后设置好下拉选项
最后一步就是点击呼出了:
openPlugin() {
if (plugin.type === 'app') {
// 呼出 app
execSync(plugin.action);
commit('commonUpdate', {
selected: null,
showMain: false,
options: [],
searchValue: '',
});
ipcRenderer.send('changeWindowSize-rubick', {
height: getWindowHeight([]),
});
return;
}
}
最后来看一下系统app检索效果:
结语
本篇主要介绍如何实现一个类似于 utools 的插件搜索功能,当然这远远不是 utools 的全部,下期我们再继续介绍如何实现 utools 其他能力。欢迎大家前往体验 Rubick 有问题可以随时提 issue 我们会及时反馈。
另外,如果觉得设计实现思路对你有用,也欢迎给个 Star:https://github.com/clouDr-f2e/rubick
大型情感类技术连续剧-徒手撸一个 uTools(二)的更多相关文章
- 徒手撸一个 Spring Boot 中的 Starter ,解密自动化配置黑魔法!
我们使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中.Starter 为我们带来了众多的自动化配置,有了这些自动化配置,我们可以不费吹灰之力就能搭建一个生产级开发环境,有的小 ...
- 徒手撸一个简单的RPC框架
来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...
- 大型情感类电视连续剧--Android高德之旅(3)地图交互
总要说两句 前两篇讲到了地图的基础显示和地图类型,今天来记录下高德地图交互相关的设置.地图的绘制分很多层,层级的显示需要根据不同的场景来设置.地图的触摸事件也很丰富,有单击.双击.单指拖拽.双指拖拽. ...
- 从零开始徒手撸一个vue的toast弹窗组件
相信普通的vue组件大家都会写,定义 -> 引入 -> 注册 -> 使用,行云流水,一气呵成,但是如果我们今天是要自定义一个弹窗组件呢? 首先,我们来分析一下弹窗组件的特性(需求): ...
- 大型情感类电视连续剧--Android高德之旅(2)地图类型
总要说两句 今天继续我们的Android高德之旅,上一篇已经能够显示最主要的地图了.有主要的放大缩小功能.还有最后做的点击3D旋转.倾斜视角的效果.今天这篇文章来记录一下高德地图的5种地图类型. (其 ...
- 使用 Go 语言徒手撸一个负载均衡器
负载均衡器在 Web 架构中扮演着非常重要的角色,被用于为多个后端分发流量负载,提升服务的伸缩性.负载均衡器后面配置了多个服务,在某个服务发生故障时,负载均衡器可以很快地选择另一个可用的服务,所以整体 ...
- 手把手教你用netty撸一个ZkClient
原文地址: https://juejin.im/post/5dd296c0e51d4508182449a6 前言 有这个想法的缘由是前一阵子突发奇想, 想尝试能不能直接利用js连接到zookeeper ...
- 徒手生撸一个验证框架,API 参数校验不再怕!
你们之中大概率早已练就了代码的拷贝.粘贴,无敌的码农神功,其实做久了业务功能开发,练就这两个无敌神功,那是迟早的事儿.今天先抛一个小问题,来打通你的任督二脉,就是很好奇的问一下:业务功能开发中,输入参 ...
- 看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么我们要去造轮子? 造轮子的核心目的,是为了解决通用共性问题的凝练和复用. 虽然 ...
随机推荐
- Git安装教程最新版本(国内gitee国外github)
Git安装教程最新版本(国内gitee国外github) 欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 获取大师使用的typora主题: http://w ...
- 转圈 箭头 ⟳ 10227 27F3 刷新 HTML常用的特殊符号总结
HTML常用的特殊符号总结 2014年9月12日 57621次浏览 html中经常会用到一些特殊符号,例如箭头,雪花,心形等等,这些符号就不用css样式或者图片来写了,直接用html特殊符号可以实现. ...
- Linux利用nc命令脚本批量检测服务器指定端口是否开放
一.nc命令检测端口的用法 # nc -v -w 10 %IP% -z %PORT% -v 显示指令执行过程. -w <超时秒数> 设置等待连线的时间. -u 表示使用UDP协议 -z 使 ...
- 进入除错模式!进入此模式后,将会出现更多的选项,分别是: · 以基本图形介面安装 CentOS 7 (使用标准显卡来设定安装流程图示); · 救援Centos系统; · 执行内存测试(Run a memory test);
Centos 7.3 安装 0.0392017.07.14 20:12:09字数 1550阅读 985 Centos 7.3 基于 Red Hat 企业版的源代码的最新版本的 CentOS 7 在今年 ...
- mysql 无法执行select查询
场景:mysql无法执行select命令查询,对于已存在的数据库,除了mysql.information_schema数据库,其它诸如nova.keystone.cinder等数据库都有此现象. 日志 ...
- Redis 主从架构搭建
引言 准备搭建的是主从架构( Master/Slave )中的一主两从模式:其中 Master 为 Redis 的主服务器,主要负责写操作,两个 Slave 为 Redis 的从服务器,主要负责读操作 ...
- 进程-信号相关 函数-(转自wblyuyang)
Linux 中的进程: 程序时一个预定义的指令序列,用来完成一个特定的任务. C 编译器可以把每个源文件翻译成一个目标文件,链接器将所有的目标文件与一些必要的库链接在一起,产生一个可执行文件.当程序被 ...
- sizeof 的妙用
(1)sizeof也可以对一个函数调用求值,其结果是函数返回类型的大小,函数并不会被调用.
- selenium常用代码
from selenium import webdriver # 1. 添加浏览器设置参数对象 options = webdriver.ChromeOptions() # 2. 设置中文,与下载无弹窗 ...
- System Verilog设计例化和连接