app.window - 窗口管理 API
列出、查询、移动、调整大小及控制 macOS 应用窗口。
权限: L2 敏感级,需要系统辅助功能(Accessibility)权限。首次调用时系统会提示授权。
方法
app.window.list(opts?)
获取所有可见窗口列表。
参数:
opts(table, 可选):app(string) - 按应用名称过滤(支持部分匹配)
返回值: array<table> - 每项包含:
title(string) - 窗口标题app(string) - 应用名称bundleId(string) - 应用 Bundle IDpid(number) - 进程 IDx(number) - 窗口左上角 X 坐标(点)y(number) - 窗口左上角 Y 坐标(点)width(number) - 窗口宽度(点)height(number) - 窗口高度(点)isMinimized(boolean) - 是否已最小化isFullscreen(boolean) - 是否全屏
-- 获取所有窗口
local windows = app.window.list()
for _, w in ipairs(windows) do
app.log.info(w.app .. ": " .. w.title)
end
-- 仅获取 Safari 的窗口
local safariWindows = app.window.list({app = "Safari"})
app.window.focused()
获取当前聚焦(最前端)的窗口。
返回值: table|nil - 窗口信息(字段同 list() 返回项),无焦点窗口时返回 nil
local win = app.window.focused()
if win then
app.log.info("当前窗口: " .. win.title .. " (" .. win.app .. ")")
end
app.window.move(window, x, y)
移动窗口到指定位置。
参数:
window- 窗口标识,支持以下三种形式:{app = "Safari", title = "窗口标题"}- 按应用名和标题匹配{pid = 1234, title = "窗口标题"}- 按进程 ID 和标题匹配"窗口标题"- 仅按标题匹配(取第一个匹配项)
x(number) - 目标 X 坐标(点)y(number) - 目标 Y 坐标(点)
返回值: boolean, error
local ok, err = app.window.move({app = "Safari"}, 0, 0)
if not ok then
app.log.error("移动失败: " .. (err or ""))
end
-- 使用进程 ID
app.window.move({pid = 1234, title = "index.html"}, 100, 100)
-- 仅按标题匹配
app.window.move("Untitled", 200, 200)
app.window.resize(window, w, h)
调整窗口大小。
参数:
window- 窗口标识(同move())w(number) - 目标宽度(点)h(number) - 目标高度(点)
返回值: boolean, error
local ok, err = app.window.resize({app = "Finder"}, 800, 600)
app.window.setFrame(window, x, y, w, h)
同时设置窗口位置和大小。
参数:
window- 窗口标识(同move())x(number) - X 坐标y(number) - Y 坐标w(number) - 宽度h(number) - 高度
返回值: boolean, error
-- 将 Safari 设为左半屏
local screen = app.screen.mainScreen()
local ok, err = app.window.setFrame(
{app = "Safari"},
0, 0,
screen.width / 2, screen.height
)
app.window.minimize(window)
最小化窗口。
参数:
window- 窗口标识(同move())
返回值: boolean, error
app.window.minimize({app = "Safari"})
app.window.maximize(window)
最大化窗口(缩放到填充屏幕可用区域)。
参数:
window- 窗口标识(同move())
返回值: boolean, error
app.window.maximize({app = "Safari"})
app.window.close(window)
关闭窗口。
参数:
window- 窗口标识(同move())
返回值: boolean, error
app.window.close({app = "TextEdit", title = "Untitled"})
app.window.focus(window)
将窗口置于最前并聚焦。
参数:
window- 窗口标识(同move())
返回值: boolean, error
app.window.focus({app = "Finder"})
AX 元素(1.3.0)
app.window.element.* 子模块暴露辅助功能元素树,可用于读取控件状态、定位按钮/输入框、模拟点击与写入。所有函数共用 window 权限和辅助功能授权。
元素句柄(handle)形如 {__ax_element_id, __type = "AXElement", role, title, identifier, value, children},Lua 侧当作普通 table 读取即可,但必须原封不动地传给 click/value/setValue。
app.window.element.tree(window, opts?)
递归读取窗口下的 AX 元素树(since 1.3.0)。
参数:
window- 窗口描述(同app.window.move())opts(table, 可选)maxDepth(number) - 递归深度(默认 10)maxChildren(number) - 每层子元素上限(默认 200)
返回值: handle - 根节点,包含 children 嵌套数组
local tree = app.window.element.tree({app = "Calculator"})
for _, btn in ipairs(tree.children) do
app.log.info(btn.role .. " / " .. btn.title)
end
app.window.element.find(window, filter)
按条件在元素树中搜索,返回匹配的句柄数组(since 1.3.0)。
参数:
window- 窗口描述filter(table)role(string, 可选) - 精确匹配 AXRole(如"AXButton")title(string, 可选) - title 包含指定子串(不区分大小写)identifier(string, 可选) - 精确匹配 AXIdentifierlimit(number, 可选) - 最多返回项数(默认 50)maxDepth(number, 可选) - 搜索深度(默认 15)
返回值: array<handle>, err
local buttons = app.window.element.find({app = "Calculator"}, {
role = "AXButton", title = "1"
})
app.window.element.click(handle)
触发元素的 AXPress 动作(since 1.3.0)。
local ok = app.window.element.click(buttons[1])
app.window.element.value(handle)
读取元素的 AXValue(since 1.3.0)。CGPoint/CGSize/CGRect/CFRange 会被转换成 table;其它保持字符串或数字。
local v = app.window.element.value(handle)
app.window.element.setValue(handle, value)
设置元素的 AXValue(since 1.3.0)。主要用于输入框、滑块等可写控件。
app.window.element.setValue(inputField, "hello")
说明
- 所有窗口操作均需要系统辅助功能权限,可在「系统设置 → 隐私与安全性 → 辅助功能」中授权
- 窗口标识中
title支持部分匹配;若多个窗口匹配,操作第一个匹配项 maximize()等同于双击标题栏的缩放按钮,不同于全屏模式- 坐标系以主显示器左上角为原点,向右向下为正方向(点单位,非像素)
- 最小化的窗口无法通过
move()/resize()操作,需先调用focus()恢复
示例
整理窗口布局
function MyPlugin:handleOrganizeWindows(context)
local screen = app.screen.mainScreen()
local w = screen.width / 2
local h = screen.height
-- Safari 放左半屏
app.window.setFrame({app = "Safari"}, 0, 0, w, h)
-- VS Code 放右半屏
app.window.setFrame({app = "Code"}, w, 0, w, h)
app.notification.show("窗口整理完成", "左: Safari,右: VS Code")
end
列出所有窗口信息
function MyPlugin:handleListWindows(context)
local windows = app.window.list()
local lines = {}
for _, win in ipairs(windows) do
local size = string.format("%dx%d @ (%d,%d)", win.width, win.height, win.x, win.y)
table.insert(lines, string.format("[%s] %s — %s", win.app, win.title, size))
end
local result = table.concat(lines, "\n")
app.dialog.alert("当前窗口 (" .. #windows .. ")", result)
end