外观
启动器
启动器——如 Spotlight、Raycast、Alfred、PowerToys Run 或 Ulauncher——可以 在任何地方用快捷键唤起,在用户当前的应用之上接收其输入,并且来得快去得也 快。MyGo 具备各个部件:用于显示它的面板、用于唤起它的 全局快捷键、可供搜索的 已安装应用及其图标,以及一个没有 Dock 图标、栖身于 托盘的应用。 examples/launcher 用原生 UI 把它们组合在一起:
sh
go run ./examples/launcher后台应用
启动器没有 Dock 图标,也不出现在应用切换器中,无窗口显示地运行,并且只 运行一个实例:
go
func main() {
if !mygo.App.RequestSingleInstanceLock() {
return // the launcher that runs shows itself
}
mygo.App.SetActivationPolicy(mygo.ActivationPolicyAccessory)
mygo.App.OnWindowAllClosed(func() {})
mygo.App.OnSecondInstance(func([]string, string) { show() })
mygo.App.WhenReady(ready)
if err := mygo.App.Run(); err != nil {
log.Fatal(err)
}
}打包后的应用从一开始就声明这一点,使 macOS 永不把它显示在 Dock 中,方法是 在 mygo.json 中设置 LSUIElement:
json
{
"macos": { "infoPlist": { "LSUIElement": true } }
}托盘图标给了用户一条除快捷键之外的入口,也用于退出; App.SetOpenAtLogin 让启动器随会话启动,而 App.WasOpenedAtLogin 会告诉它此时应保持隐藏。
面板
面板悬浮在所有应用之上,包括全屏应用,并跟随用户所在的任意 Space 或虚拟 桌面;在 macOS 上,它获得键盘而无需激活启动器:用户原来的应用保留其菜单 栏,面板隐藏后键盘即归还。创建它时就隐藏,只创建一次,并且隐藏它而不是 关闭它,这样它能原样立即再次显示:
go
panel := mygo.NewWindow(mygo.WindowOptions{
Panel: true,
Hidden: true,
Frameless: true,
Transparent: true,
Vibrancy: mygo.VibrancyGlass, // Liquid Glass on macOS 26, Acrylic on Windows 11
CornerRadius: 16,
Width: 680,
Height: 420,
DisableResize: true,
Content: ui.View(app.view), // or URL: "/launcher"
})
panel.OnClose(func(e *mygo.CloseEvent) { // Escape, Cmd-W, Alt-F4
e.PreventDefault()
panel.Hide()
})
panel.OnBlur(panel.Hide) // the user went elsewhere页面或原生 UI 未处理的 Escape 键会关闭面板,OnClose 监听器把关闭变成 隐藏。若 Escape 需要先做别的事,就自己处理它,如示例中先清空查询。
把它显示在用户正在查看的显示器上——指针的位置可以说明这一点——按 Spotlight 的方式放在自上而下三分之一处,并让搜索框获得键盘:
go
func show() {
d := mygo.Screen.DisplayNearestPoint(mygo.Screen.CursorScreenPoint())
w, h := panel.Size()
panel.SetPosition(d.WorkArea.X+(d.WorkArea.Width-w)/2, d.WorkArea.Y+(d.WorkArea.Height-h)/3)
app.query = ""
app.input.Focus()
panel.Show()
}快捷键
go
err := mygo.GlobalShortcut.Register("Alt+Shift+Space", func() {
if panel.IsVisible() {
panel.Hide()
} else {
show()
}
})选一个其他启动器没有占用的:Raycast 和 PowerToys Run 占用 Alt+Space, Spotlight 占用 Cmd+Space,而 macOS 会把快捷键分配给每个注册它的应用,那样 两者都会打开。让用户在启动器的设置中另选一个:Unregister 旧的并 Register 新的,当 Register 因快捷键被占用而失败时告知用户。在 Windows 上,按键使面板获得前台;在 Wayland 上由桌面绑定快捷键,见 全局快捷键。
查找应用
Shell.InstalledApps 列出计算机上安装的应用程序,按名称排序,并使用系统 以用户语言显示的名称。它会读取磁盘,耗时几十毫秒:在面板显示时于一个 goroutine 上列出它们,并通过 Window.Update 交给视图(或通过绑定的服务 交给页面):
go
go func() {
apps, err := mygo.Shell.InstalledApps()
if err != nil {
log.Print(err)
}
panel.Update(func() {
app.apps = apps
app.filter()
})
}()Shell.FileIcon(app.Path, size) 将应用图标绘制为 PNG,以像素计:即 DIP 尺寸乘以显示器的缩放比例。它耗时几毫秒:只绘制可见行的图标,并按路径缓存 它们。Shell.LaunchApp(app) 启动所选应用,或将其带到前台;先隐藏面板, 让用户原来的应用持有键盘,直到被启动的应用接管。
平台
| macOS | Windows | Linux | |
|---|---|---|---|
| 无 Dock 图标、无任务栏按钮 | LSUIElement、ActivationPolicyAccessory | 面板没有 | 面板没有 |
| 面板 | 不激活的 NSPanel,位于普通应用和附属应用的全屏应用之上 | 置顶的工具窗口 | X11 上保持置顶的实用窗口;在 Wayland 合成器支持时使用 layer surface(需 libgtk-layer-shell.so.0) |
| 快捷键 | Carbon 热键 | RegisterHotKey | X11 抓取,Wayland 下的 XDG 桌面门户 |
| 应用 | "应用程序"文件夹 | 开始菜单的"所有应用" | 桌面入口 |
在 Wayland 上,作为 layer surface 的面板在显示期间持有键盘,当地启动器都 如此,因此它只在隐藏时才失焦:在 Escape 时关闭它。