 |
|
3
loganovo 9h 55m ago
又优化了一下, 应该是最终版了:
1. 窗口大小判断移动到 AppleScript 执行之前, 减少开销 2. 增强健壮性, 预防极端条件的竟态情况 3. 设置窗口的宽高放到外部设置, 另解释: 是逻辑(非物理)像素距离,需要使用 mac 系统自带的截图工具测量距离 4. 优化日志
``` -- 统一日志打印控制开关 -- 将其设置为 false 即可关闭此脚本的所有输出 ENABLE_DEBUG_LOG = false
-- 自定义日志包装方法, 支持传入多个参数 function debugLog(...) if ENABLE_DEBUG_LOG then print(...) end end
-- 2 、finder 窗口拦截修改宽高脚本 -- 动态获取系统 Finder 的本地化名称 (中文系统下返回 "访达", 英文返回 "Finder") -- 这样写可以彻底避免硬编码字符串带来的匹配失败问题 -- 动态获取系统中当前正在运行的 Finder 进程对象 local finderApp = hs.application.get("com.apple.finder") -- 从运行的进程中提取它在当前系统语言下实际显示的名称 -- 如果是中文系统, finderApp:name() 将 100% 准确地返回 "访达" FINDER_APP_NAME = finderApp and finderApp:name() or "Finder"
-- 设置窗口的宽高: 逻辑(非物理)像素距离,需要使用 mac 系统自带的截图工具测量距离 FINDER_APP_W = 803 FINDER_APP_H = 625
-- 初始化窗口过滤器: 传入 false 表示默认拒绝系统中的所有窗口事件, 节约性能 FinderFilter = hs.window.filter.new(false)
-- 使用 setAppFilter 专门针对 Finder 设定规则: -- 1. allowRoles = 'AXStandardWindow': 底层直接屏蔽偏好设置, 进度条等非标准面板. -- 2. 故意不写 visible 参数: 默认为 nil (忽略), 这意味着即使是刚创建还未渲染显示出来的底层窗口也能被捕获. -- 使用 setAppFilter 专门针对 Finder 设定规则 FinderFilter:setAppFilter(FINDER_APP_NAME, { allowRoles = 'AXStandardWindow', rejectTitles = { -- 精准匹配设置面板 "^" .. FINDER_APP_NAME .. "设置$", "^" .. FINDER_APP_NAME .. "偏好设置$", "^" .. FINDER_APP_NAME .. " Settings$", "^" .. FINDER_APP_NAME .. " Preferences$", -- 新增: 利用 Lua 模式匹配 (以特定字符串结尾的正则 $ 锚点) -- 拦截所有标题以 "简介" 或 " Info" 结尾的窗口 "简介$", " Info$" } })
-- 订阅窗口创建事件 FinderFilter:subscribe(hs.window.filter.windowCreated, function(win) -- 此时能触发该函数的, 必定是 Finder 的标准窗口 debugLog("检测到新建标准窗口:" .. tostring(win:title())) -- 安全与类型检查: -- 1. 确保 win 对象及其底层的窗口 ID 真实存在 -- 2. isStandard() 确保这是一个标准窗口, 而非偏好设置或进度条面板 if win and win:id() and win:isStandard() then debugLog("确定这是一个标准窗口") -- 引入 0.05 秒延迟, 等待 macOS 彻底赋予窗口宽高坐标数据 hs.timer.doAfter(0.05, function() -- 安全校验: 确保句柄存在 if win and win:id() then debugLog("确定句柄存在")
local currentSize = win:size() local tolerance = 5
-- 模糊宽高校验 if math.abs(currentSize.w - FINDER_APP_W) <= tolerance and math.abs(currentSize.h - FINDER_APP_H) <= tolerance then debugLog("[拦截] 窗口尺寸已符合要求, 跳过调整") return end
if currentSize.w < FINDER_APP_W and (FINDER_APP_W - currentSize.w) > 300 then debugLog("[拦截] 当前窗口尺寸很小, 认为不是文件浏览窗口, 跳过调整") return end
-- 记录当前 Hammerspoon 捕获到的窗口标题, 用于后续校验 local currentTitle = win:title()
-- 编写 AppleScript: 提取当前最前方窗口的 class 和 name, 并作为列表返回 local asCmd = [[ tell application "Finder" if (count of windows) is 0 then return {"none", "none"} set fw to front window set wClass to class of fw as string set wName to name of fw as string return {wClass, wName} end tell ]]
-- 使用 Hammerspoon 的 hs.osascript 模块向 Finder 发起底层查询 local ok, result = hs.osascript.applescript(asCmd)
-- 严格过滤: 确保返回的类型是表, 且包含 class 和 name 数据 if not ok or type(result) ~= "table" or #result ~= 2 then debugLog("[拦截] AppleScript 执行失败或未获取到有效窗口数据 -> 跳过调整") return end
local windowClass = result[1] local windowName = result[2]
-- 双重校验: 标题必须绝对相等 (排除竞态条件), 且底层类名必须是 "Finder window" if windowClass ~= "Finder window" or windowName ~= currentTitle then debugLog("[拦截] 非浏览窗口或触发竞态条件. 顶层活跃窗口: class=" .. tostring(windowClass) .. ", title=" .. tostring(windowName) .. " -> 跳过调整") return end
debugLog("确认是文件浏览窗口, 准备调整尺寸. 顶层活跃窗口: class=" .. tostring(windowClass) .. ", title=" .. tostring(windowName))
-- 记录当前动画时间并强制设为 0 (实现无缝缩放) local defaultAnimationDuration = hs.window.animationDuration hs.window.animationDuration = 0
win:setSize({ w = FINDER_APP_W, h = FINDER_APP_H })
-- 恢复系统动画时间 hs.window.animationDuration = defaultAnimationDuration debugLog("窗口尺寸调整完毕") end end) end end)
```
|