「スペースバーが押されているあいだだけ手のひらツールに変化させるのが大変だった」という話を耳にしたので自分も挑戦してみた。もっときれいな方法や間違いがあったら教えてください。
以下、解説。
ツールの切り替え
スペースバーが押されているあいだだけ手のひらツールに変化させ、それ以外にはユーザの選択したツールが有効になるのがよくあるパターンなので再現してみる。
tool
(ユーザが選択するツール)と effectiveTool
(実際に有効なツール)を別プロパティとして用意。
keyDown(with:)
と keyUp(with:)
のタイミングで isSpaceBarPressed
(スペースバーが押されているかのフラグ)を操作。
- フラグが変更されたらそれらをもとに
effectiveTool
を更新。
キーボードイベントが受け取れるようにビューがレスポンダチェーンに存在する必要があるはず。
カーソルの変更
Cocoa におけるカーソルの変更は独特なので注意。ビューには cursor
みたいなプロパティがなくて直接「現在のカーソル」を NSCursor.set()
で切り替えるのだが、好きなタイミングでセットしてもほかの要因ですぐに上書きされてしまう。
ビューの cursorUpdate(with:)
をオーバーライドして、状態やポイントされている位置に応じた NSCursor
をセットする処理を実装しておきそれが適切なタイミングで呼ばれるようにするのが基本だ。
init(...)
で NSTrackingArea
を追加しておく。カーソルの更新が目的なので .cursorUpdate
オプションは必須。さらに .inVisibleRect
を指定すればサイズが変わるたびに追加しなおす必要がなくなる。
- するとマウスが触れたり離れたりしたときに
cursorUpdate(with:)
が呼ばれるようになるので、そこで effectiveTool
に応じた NSCursor
をセット。
effectiveTool
が変更されたときにウインドウの invalidateCursorRects(for:)
を呼ぶようにすれば、そこでも cursorUpdate(with:)
が呼ばれる。
その他の工夫
mouseDragged(with:)
を使ってドラッグイベントを捉えようとするとカーソルがウインドウの外に出たときに変化してしまうので trackEvents(matching:timeout:mode:handler:)
を使って余計なイベントが割り込んでこないようにしてみた。下手な実装をするとループから抜け出せないことがあるので注意。
hitTest(_:)
をオーバーライドして、手のひらツールのときは子孫ビューにマウスイベントが行かないようにする。
Share
リンクも共有もお気軽に。記事を書くモチベーションの向上に役立てます。