再現コード:
(progn (require 'svg) (goto-char (point-min)) ;; (insert "\n") ;;これがあると発生しづらい(スクロールして画像を一番上にもってくれば発生する) (let ((ov (make-overlay (point) (progn (insert " ") (point)) nil t))) (overlay-put ov 'evaporate t) (overlay-put ov 'pointer 'arrow) (overlay-put ov 'line-prefix "XXX") ;;これが無ければ発生しない(実際の状況ではこれが無くてもorg-indentがテキストプロパティのline-prefixを変更するといったことでこれと同じ事になる) (overlay-put ov 'display (let ((svg (svg-create 400 300))) (svg-rectangle svg 0 0 400 300 :fill "blue") (svg-text svg "Click here" :x 200 :y 150 :text-anchor "middle" :fill "white") (svg-image svg))) (overlay-put ov 'keymap (let ((km (make-sparse-keymap))) (define-key km [mouse-1] (lambda (ev) (interactive "e") (message "xy=%s" (posn-object-x-y (event-start ev))))) km))) (insert "\n"))
上のコードをscratchバッファで実行すると画像が表示される。その画像をクリックするとミニバッファに座標が表示されるが、画像の左上からの座標になっていないことが分かる。原点は画像の左上では無く「XXX」の左上になっている。
この現象は画像がウィンドウの先頭にあり、その左にline-prefixによる文字列があるときのみ起こる。(line-prefixはテキストプロパティ、オーバーレイプロパティ、line-prefix変数のいずれで設定しても現象は起きる)
Emacs Lisp Reference Manualの22.7.4 Click Eventsには、マウスイベントのdx,dyはobjectの左上角を(0 . 0)とする相対座標と書かれている。今回の場合はobjectは画像('imageで始まるdisplayプロパティ)になるので、画像の左上からの座標になっていなければならない。しかし、画像がウィンドウの先頭にあって、かつ、line-prefixが使われている時に限り、line-prefix文字列の左上からの座標になってしまっている。画像がウィンドウの先頭に無いときや、line-prefixが使われていないときは正しく画像左上からの座標になっている。動作に一貫性が無く、文書化されている仕様と異なる結果が生じているのでEmacsの問題と言って良いのではないかと思う。
回避策としては、マウスの入力を必要とする時だけline-prefixを無効化するといったことが考えられる。
実際にel-easydrawにおいて作図エディタがウィンドウの一番上にあるときに座標がずれる問題が発生していた。org-indentを有効にすると発生する。org-indentはline-prefixやwrap-prefixを使用するので、まさに今回のケースに当てはまる。コミット701bfaaにて一時的にline-prefixを無効化することで回避した。
おそらくEmacsのバグだと思うが、この問題に気がつく人はどのくらいいるのだろうか。Emacs内に画像を表示してその中に対してマウス入力するなんてことを伊達や酔狂で無く本気でやっている人はどのくらいいるのだろうか。
[…] 以前にもline-prefixでマウス入力の座標がずれる問題に遭遇したことがある。Emacsのソースコードを確認していないが、この辺りの処理には何らかの構造的な問題があるのかもしれない。私の経験的にもレイアウト処理というのはちゃんと設計しないと複雑怪奇なものになりがちだ。 […]