Hammerspoon – init.lua

キーリマップの機能をHammerspoonに移植しましたので貼っておきます。

機能

  • メニューバー上でのホイール操作で音量UP/Down
  • Option+タイトルバークリックでウィンドウを最背面に移動(Zオーダー変更)
  • Option+クライアント領域ドラッグでウィンドウの移動

作ったもの

Hammerspoonのメニューアイコンで「Open Config」を選択し、以下をコピペしてください。

-- Spoonの読み込み
local spoonList = {
    "ReloadConfiguration",
    "MiddleClickDragScroll",
    -- "VolumeScroll",
}
for _, name in ipairs(spoonList) do
    hs.loadSpoon(name)
end

-- 各Spoonの設定と起動
if spoon.ReloadConfiguration then
    spoon.ReloadConfiguration:start()
end

if spoon.MiddleClickDragScroll then
    spoon.MiddleClickDragScroll:start()
end

-- if spoon.VolumeScroll then
--     spoon.VolumeScroll:start()
-- end


-- 自作処理
myConfig = myConfig or {}

--[[---------------------------------------------------------------------
VolumeScrollの代替処理
-----------------------------------------------------------------------]]
local MENU_BAR_HEIGHT = 25

myConfig.volWheelTap = hs.eventtap.new({hs.eventtap.event.types.scrollWheel}, function(event)
    local mousePos = hs.mouse.absolutePosition()
    if mousePos.y <= MENU_BAR_HEIGHT then
        local scroll = event:getProperty(hs.eventtap.event.properties.scrollWheelEventDeltaAxis1)
        if scroll ~= 0 then
            local key = (scroll > 0) and "SOUND_UP" or "SOUND_DOWN"
            hs.eventtap.event.newSystemKeyEvent(key, true):post()
            hs.eventtap.event.newSystemKeyEvent(key, false):post()            
            return true    
        end
    end
    return false
end)
myConfig.volWheelTap:start()

--[[---------------------------------------------------------------------
ウィンドウのドラッグ処理
-----------------------------------------------------------------------]]

-- 設定定数
local TITLE_BAR_HEIGHT = 40
local THRESHOLD_H = 15
local THRESHOLD_V = 15
local DRAG_DEADZONE = 0

hs.window.animationDuration = 0

-- 座標が矩形内にあるか判定
function isPointInRect(rect, point)
    if not rect or not point then return false end
    return point.x >= rect.x and 
           point.x <= (rect.x + rect.w) and 
           point.y >= rect.y and 
           point.y <= (rect.y + rect.h)
end

-- マウスの下にあるウィンドウを取得
function getWindowUnderMouse()
    local pos = hs.mouse.absolutePosition()
    -- Zオーダー順(手前にある順)に全ウィンドウを取得
    local allWindows = hs.window.orderedWindows()
    
    for _, win in ipairs(allWindows) do
        local frame = win:frame()
        -- マウス座標がウィンドウの枠内に入っているか判定
        if pos.x >= frame.x and pos.x <= (frame.x + frame.w) and
           pos.y >= frame.y and pos.y <= (frame.y + frame.h) then
            return win
        end
    end
    return nil
end

myConfig.windowActionTap = hs.eventtap.new({
    hs.eventtap.event.types.leftMouseDown,
    hs.eventtap.event.types.leftMouseDragged
}, function(event)
    local flags = event:getFlags()
    --NOTE:masOS標準アプリで以下の機能と競合する場合がある
    --Option+クリック: 現在アクティブなアプリ以外をすべて非表示
    if not flags.alt then return false end -- Optionキー必須

    local eventType = event:getType()
    local mousePos = hs.mouse.absolutePosition()

    -- A. クリック開始
    if eventType == hs.eventtap.event.types.leftMouseDown then
        local targetWin = getWindowUnderMouse() -- 作成した関数を再利用
        if not targetWin or targetWin:isFullScreen() then return false end

        local frame = targetWin:frame()
        if not isPointInRect(frame, mousePos) then return false end

        -- タイトルバー領域のクリックか判定
        if mousePos.y <= (frame.y + TITLE_BAR_HEIGHT) then
            -- 背面送り
            local allWindows = hs.window.orderedWindows()
            if #allWindows > 1 then
                targetWin:sendToBack()
                -- 送った瞬間に一瞬フォーカスが浮くのを防ぐため、
                -- 0.01秒待ってから「今マウスの下にある別の窓」をアクティブにする
                hs.timer.doAfter(0.01, function()
                    local nextWin = getWindowUnderMouse()
                    nextWin:focus()
                end)
            end
            return true -- クリックを消費して終了
        else
            -- ドラッグ移動準備
            myConfig.drag = {
                win = targetWin,
                startMouse = mousePos,
                startWinOrigin = {x = frame.x, y = frame.y}
            }
            return true
        end
    end

    -- B. ドラッグ中
    if eventType == hs.eventtap.event.types.leftMouseDragged and myConfig.drag then
        local drag = myConfig.drag
        local mousePos = event:location()

        -- 移動量を計算
        local dx = mousePos.x - drag.startMouse.x
        local dy = mousePos.y - drag.startMouse.y
        local distance = math.sqrt(dx^2 + dy^2)

        local nextPos = {x = drag.startWinOrigin.x + dx, y = drag.startWinOrigin.y + dy}

        -- 指定距離(DEADZONE)以上動いた場合のみ、角度補正(スナップ)を適用
        if distance > DRAG_DEADZONE then
            local angleRad = math.atan2(-dy, dx)
            local angleDeg = math.abs(math.deg(angleRad))

            if angleDeg <= THRESHOLD_H or angleDeg >= (180 - THRESHOLD_H) then
                -- 水平ロック
                nextPos.y = drag.startWinOrigin.y
            elseif math.abs(angleDeg - 90) <= THRESHOLD_V then
                -- 垂直ロック
                nextPos.x = drag.startWinOrigin.x
            end
        end

        -- ウィンドウを移動
        drag.win:move({nextPos.x - drag.win:frame().x, nextPos.y - drag.win:frame().y}, nil, false, 0)
        return true
    end
    return false
end):start()

-- マウスアップでリセット
myConfig.dragUpTap = hs.eventtap.new({hs.eventtap.event.types.leftMouseUp}, function(event)
    myConfig.drag = nil
    return false
end):start()

利用ライブラリ(任意)

ReloadConfiguration:init.luaファイルの自動リロード
MiddleClickDragScroll:マウスホイールのドラッグでスクロール

ダウンロードしたZipファイルを展開してReloadConfiguration.spoon、MiddleClickDragScroll.spoonをダブルクリックすると
~/.hammerspoon/Spoons
配下にインストールされます。

※利用ライブラリをインストールしない場合は以下の処理を削除して、メニューアイコンの「Reload Config」で反映してください。

-- Spoonの読み込み
local spoonList = {
    "ReloadConfiguration",
    "MiddleClickDragScroll",
    -- "VolumeScroll",
}
for _, name in ipairs(spoonList) do
    hs.loadSpoon(name)
end

-- 各Spoonの設定と起動
if spoon.ReloadConfiguration then
    spoon.ReloadConfiguration:start()
end

if spoon.MiddleClickDragScroll then
    spoon.MiddleClickDragScroll:start()
end

-- if spoon.VolumeScroll then
--     spoon.VolumeScroll:start()
-- end

既知の不具合

  • Zオーダー変更時に画面がチラつく
  • ウィンドウのドラッグ時に移動にもたつくアプリがある

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です