Yearly Archives: 2025

2025-07-27

VerticoとCorfuをタッチスクリーンで操作できるようにする

私はEmacsの補完インタフェースにVerticoとCorfuを使用していますが、Android版のEmacsを使っていると補完候補の一覧をタップで選択できないことにフラストレーションを感じます。同様にスワイプによるスクロールも出来ません。

というわけで、何とかしてみました。

Vertico用のコード:

;;; my-vertico-touch.el ---                          -*- lexical-binding: t; -*-

;; 使い方:
;; (with-eval-after-load 'vertico
;;   (require 'my-vertico-touch)
;;   (my-vertico-touch-setup)

(require 'vertico)
(require 'vertico-mouse)

(defconst my-vertico-touch-tap-threshold 4)

(defun my-vertico-touchscreen-begin (begin-event)
  (interactive "e")
  (let* ((begin-posn (cdadr begin-event))
         (begin-xy (posn-x-y begin-posn))
         (begin-window (posn-window begin-posn))
         (moved nil))
    (with-selected-window begin-window
      (let ((begin-scroll-pos vertico--scroll))
        (while
            (let ((ev (read-event)))
              (pcase (car-safe ev)
                ('touchscreen-update
                 (let* ((update-xy (touch-screen-relative-xy (cdaadr ev)
                                                             begin-window))
                        (dx (- (car update-xy) (car begin-xy)))
                        (dy (- (cdr update-xy) (cdr begin-xy))))
                   (when (and (not moved)
                              (>= (+ (* dx dx) (* dy dy))
                                  (* my-vertico-touch-tap-threshold
                                     my-vertico-touch-tap-threshold)))
                     (setq moved t))
                   (when moved
                     (let* ((dline (/ dy (default-line-height)))
                            (new-scroll-pos (- begin-scroll-pos dline)))
                       (cond
                        ((< new-scroll-pos vertico--scroll)
                         (vertico--goto (+ new-scroll-pos vertico-scroll-margin)))
                        ((> new-scroll-pos vertico--scroll)
                         (vertico--goto (+ new-scroll-pos vertico-count
                                           (- vertico-scroll-margin))))))
                     (vertico--exhibit)))
                 t)
                ('touchscreen-end
                 (unless moved
                   (vertico--goto (vertico-mouse--index begin-event))
                   (vertico-exit))
                 nil))))))))

(defun my-vertico-touch-setup ()
  (interactive)
  (vertico-mouse-mode)
  (define-key vertico-mouse-map (kbd "<touchscreen-begin>")
              #'my-vertico-touchscreen-begin))

(defun my-vertico-touch-teardown ()
  (interactive)
  (define-key vertico-mouse-map (kbd "<touchscreen-begin>")
              #'my-vertico-touchscreen-begin
              t))

(provide 'my-vertico-touch)

Corfu用のコード:

;;; my-corfu-touch.el ---                            -*- lexical-binding: t; -*-

;; 使い方:
;; (with-eval-after-load "corfu"
;;   (require 'my-corfu-touch)
;;   (my-corfu-touch-setup))

(require 'corfu)

;;;; Frame Handling

(defun my-corfu-defocus-child-frame ()
  "corfu用の子フレームからフォーカスを外す。"
  (when (eq (selected-frame) corfu--frame)
    (when-let* ((parent (frame-parent)))
      (select-frame parent))))

(defun my-corfu-handle-switch-frame-p ()
  "フレームの変更処理中なら非nilを返す。
`this-command'と`last-input-event'によって判定される。

`this-command'が`handle-switch-frame'であり、かつ、`last-input-event'が
corfu用の子フレームに対する`switch-frame'イベントであれば、非nilを返し、
そうでなければnilを返す。"
  (and (eq this-command 'handle-switch-frame)
       (eq (car-safe last-input-event) 'switch-frame)
       (eq (cadr last-input-event) corfu--frame)))

;; 子フレームがクリック/タップされたとき、`switch-frame'イベントが発生
;; し`handle-switch-frame'コマンドが実行される。また、その前後でwindow
;; 変更を通知するフックも呼び出される(クリックかタップかによって微妙に
;; 順番は変わる?)。
;;
;; それらのタイミングのどこかでフォーカスの変更が行われるので、
;; `my-corfu-defocus-child-frame'を呼び出して元の親フレームがフォーカス
;; されている状態を維持する必要がある(`switch-frame'の効果を打ち消す)。
;;
;; また、`handle-switch-frame'コマンドによってcorfuが終了してしまうこ
;; とがあるので、それも防止する必要がある。基本的にcorfuの候補表示フレー
;; ム(`corfu--frame')に対する`handle-switch-frame'は無視した方が良い。

(defun my-corfu--post-command:around (old-fun &rest args)
  (my-corfu-defocus-child-frame)

  (unless (my-corfu-handle-switch-frame-p)
    (apply old-fun args)))

(defun my-corfu--prepare:around (old-fun &rest args) ;; pre-command-hook
  (unless (my-corfu-handle-switch-frame-p)
    (apply old-fun args)))

(defun my-corfu--window-change:around (old-fun &rest args)
  ;; 注意: タッチイベントの時はhandle-switch-frameよりも先にここに来る。
  ;;       クリックの時は先にhandle-switch-frameが発生するのでこれは不要。
  (my-corfu-defocus-child-frame)
  (apply old-fun args))

;;;; Candidate List

(defun my-corfu-select (index)
  (corfu--goto index)
  (corfu-insert))

(defun my-corfu-posn-line-number (posn)
  (with-current-buffer (window-buffer (posn-window posn))
    (line-number-at-pos (posn-point posn) t)))

(defun my-corfu-posn-index (posn)
  (+ corfu--scroll (my-corfu-posn-line-number posn) -1))

(defun my-corfu-select-clicked (event)
  (interactive "e")
  (my-corfu-select (my-corfu-posn-index (event-start event))))

(defun my-corfu-set-scroll-pos (new-scroll-pos)
  (cond
   ((< new-scroll-pos corfu--scroll)
    (corfu--goto (+ new-scroll-pos corfu-scroll-margin)))
   ((> new-scroll-pos corfu--scroll)
    (corfu--goto (+ new-scroll-pos corfu-count
                    (- corfu-scroll-margin))))))

;;;; Mouse / Touch Event Handlers

(defun my-corfu-on-mouse-1 (event)
  (interactive "e")
  (my-corfu-select-clicked event))

(defconst my-corfu-touch-tap-threshold 4)

(defun my-corfu-on-touchscreen-begin (begin-event)
  (interactive "e")
  (let* ((begin-posn (cdadr begin-event))
         (begin-window (posn-window begin-posn))
         (begin-xy (posn-x-y begin-posn))
         (moved nil))
    (with-selected-window begin-window
      (let ((begin-scroll-pos corfu--scroll)
            (echo-keystrokes 0))
        (while
            (let ((ev (read-event)))
              (pcase (car-safe ev)
                ('touchscreen-update
                 (let* ((update-xy (touch-screen-relative-xy (cdaadr ev) begin-window))
                        (dx (- (car update-xy) (car begin-xy)))
                        (dy (- (cdr update-xy) (cdr begin-xy))))
                   (when (and (not moved)
                              (>= (+ (* dx dx) (* dy dy))
                                  (* my-corfu-touch-tap-threshold
                                     my-corfu-touch-tap-threshold)))
                     (setq moved t))
                   (when moved
                     (let* ((dline (/ dy (default-line-height)))
                            (new-scroll-pos (- begin-scroll-pos dline)))
                       (my-corfu-set-scroll-pos new-scroll-pos))
                     (corfu--exhibit)))
                 t)
                ('touchscreen-end
                 (unless moved
                   (my-corfu-select (my-corfu-posn-index (cdadr begin-event))))
                 nil))))))))

;;;; Setup

(defun my-corfu-touch-setup ()
  (interactive)

  ;; pre-command-hook、post-command-hook、ウィンドウ切り替え時の処理を修正する。
  (advice-add 'corfu--post-command :around 'my-corfu--post-command:around)
  (advice-add 'corfu--prepare :around 'my-corfu--prepare:around)
  (advice-add 'corfu--window-change :around 'my-corfu--window-change:around)

  ;; マウスを無視するためのキーマップにマウスやタッチのイベントハンドラを
  ;; 登録してしまう。
  (push 'my-corfu-on-mouse-1 corfu-continue-commands)
  (push 'my-corfu-on-touchscreen-begin corfu-continue-commands)
  (define-key corfu--mouse-ignore-map
              [mouse-1] #'my-corfu-on-mouse-1)
  (define-key corfu--mouse-ignore-map
              [touchscreen-begin] #'my-corfu-on-touchscreen-begin)

  ;; 子フレームにフォーカスが当たるようにする。
  ;; そうしないとイベントが起きないので。
  (setf (alist-get 'no-accept-focus corfu--frame-parameters) nil))

(provide 'my-corfu-touch)

VerticoもCorfuも同じ作者によるものなので構造は似ています。

どちらもマウスやタッチイベントは完全に無視するように作られているので、まずはそれを解除する必要があります。

Verticoの方はvertico-mouse-modeというのが付属しているので、それを参考にしてタッチイベントへの対応を追加しました。

Corfuの方は子フレームを使っているので、改善の難易度が上がります。タッチした瞬間に子フレームにフォーカスが移動し、当然カレントバッファも変わってしまいます。そのため上のコードではフォーカスを親フレームに戻す処理を入れています。そういった遷移イベントによってCorfuが終了してしまうことも防止する必要がありました。一応マウスクリックにも対応させてみましたが、ホイールへの対応はうまく出来ませんでした。フォーカスが当たっていない別フレームでホイールを回しても、ホイールイベントは発生しないようです(MS-Windowsでの使用時)。

これらのコードはMS-Windowsのタッチパネル搭載ノートPCとAndroidの両方でテストして動作することを確認しました。

設定等によってはうまく動かないケースも多々あるかもしれません。

2025-07-27 , ,

Unicodeの三角形の一覧を作成する

Unicodeの三角形ってどうなってるんだっけ? と思ったので一覧を作成してみました。

Emacsでは C-x 8 RET triangle などと入力すれば(使っている補完インタフェースにもよりますが)色々出てくるわけですが、それだと4方向分が一緒くたになっているので分かりづらいのです。なので、方向を除いたベースとなる名前が一行にまとまるように表を作ってみました。

(let ((triangle-types
       ;; 次の条件を満たすUNICODE文字を列挙する。
       ;; - 名前にTRIANGLEが含まれてる
       ;; - 名前に{LEFT|UP|RIGHT|DOWN}-POINTINGが含まれている
       ;; 結果はalist ((三角形名 . ((方向名 . コード)...))...) の形にする。
       (cl-loop with triangle-types = nil
                for name being the hash-keys of (ucs-names)
                using (hash-values code) ;; ←これ書きづらいんだけど何とかならないの?
                when (and (string-match-p "TRIANGLE" name)
                          (string-match "\\`\\(.*\\)\\(LEFT\\|UP\\|RIGHT\\|DOWN\\)\\(-POINTING .*\\)\\'" name))
                do
                (let ((base-name (concat (match-string 1 name)
                                         "*" ;; 方向の部分は * に置き換える。
                                         (match-string 3 name)))
                      (dir (match-string 2 name)))
                  (setf (alist-get dir
                                   (alist-get base-name triangle-types
                                              nil nil #'equal)
                                   nil nil #'eql)
                        code))
                finally return triangle-types)))
  ;; 表の形に文字列化する。
  (let ((dir-names  '("LEFT" "UP" "RIGHT" "DOWN")))
    (concat
     "|NAME|" (mapconcat #'identity dir-names "|") "|\n"
     "|-\n"
     (cl-loop for (name . dirs) in (nreverse triangle-types)
              concat
              (concat "|" name "|"
                      (cl-loop for dir in dir-names
                               for code = (alist-get dir dirs nil nil #'equal)
                               concat (if code (format "%X %c" code code) "-")
                               concat "|")
                      "\n")))))

(例によってこの文書はorg-modeで書かれているので、コードブロックを評価すれば自動的に↓が文書中に挿入されるわけです ( :exports both :results raw replace value を指定) )

NAME LEFT UP RIGHT DOWN
BLACK *-POINTING DOUBLE TRIANGLE 23EA ⏪ 23EB ⏫ 23E9 ⏩ 23EC ⏬
BLACK *-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR 23EE ⏮ - 23ED ⏭ -
BLACK *-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR - - 23EF ⏯ -
BLACK MEDIUM *-POINTING TRIANGLE 23F4 ⏴ 23F6 ⏶ 23F5 ⏵ 23F7 ⏷
BLACK *-POINTING TRIANGLE 25C0 ◀ 25B2 ▲ 25B6 ▶ 25BC ▼
WHITE *-POINTING TRIANGLE 25C1 ◁ 25B3 △ 25B7 ▷ 25BD ▽
BLACK *-POINTING SMALL TRIANGLE 25C2 ◂ 25B4 ▴ 25B8 ▸ 25BE ▾
WHITE *-POINTING SMALL TRIANGLE 25C3 ◃ 25B5 ▵ 25B9 ▹ 25BF ▿
WHITE *-POINTING TRIANGLE WITH DOT - 25EC ◬ - -
*-POINTING TRIANGLE WITH LEFT HALF BLACK - 25ED ◭ - 29E8 ⧨
*-POINTING TRIANGLE WITH RIGHT HALF BLACK - 25EE ◮ - 29E9 ⧩
HEAVY WHITE *-POINTING TRIANGLE - - - 26DB ⛛
BLACK MEDIUM *-POINTING TRIANGLE CENTRED 2BC7 ⯇ 2BC5 ⯅ 2BC8 ⯈ 2BC6 ⯆
*-POINTING RED TRIANGLE - 1F53A 🔺 - 1F53B 🔻
*-POINTING SMALL RED TRIANGLE - 1F53C 🔼 - 1F53D 🔽
BLACK *-POINTING ISOSCELES RIGHT TRIANGLE 1F780 🞀 1F781 🞁 1F782 🞂 1F783 🞃

シンプルな一つの三角形で4方向揃っているものに限定すると次の7種類になります。

NAME LEFT UP RIGHT DOWN
BLACK MEDIUM *-POINTING TRIANGLE 23F4 ⏴ 23F6 ⏶ 23F5 ⏵ 23F7 ⏷
BLACK *-POINTING TRIANGLE 25C0 ◀ 25B2 ▲ 25B6 ▶ 25BC ▼
WHITE *-POINTING TRIANGLE 25C1 ◁ 25B3 △ 25B7 ▷ 25BD ▽
BLACK *-POINTING SMALL TRIANGLE 25C2 ◂ 25B4 ▴ 25B8 ▸ 25BE ▾
WHITE *-POINTING SMALL TRIANGLE 25C3 ◃ 25B5 ▵ 25B9 ▹ 25BF ▿
BLACK MEDIUM *-POINTING TRIANGLE CENTRED 2BC7 ⯇ 2BC5 ⯅ 2BC8 ⯈ 2BC6 ⯆
BLACK *-POINTING ISOSCELES RIGHT TRIANGLE 1F780 🞀 1F781 🞁 1F782 🞂 1F783 🞃

基本は「BLACK *-POINTING TRIANGLE▲」と「WHITE *-POINTING TRIANGLE△」ですね。これらはJIS X 0213にも入っています(JIS X 0208の段階では上下のみ)。私の使っている環境ではstring-width関数やchar-width関数は2を返します(設定によるかもしれません)。文書中に書くのは普通はこれですが、箇条書きの先頭(bullet)に使うには大きすぎて使いづらいです。

「BLACK MEDIUM *-POINTING TRIANGLE⏶」は少し特殊で、どうも(再生ボタン等の)メディアUIで使うことを意図しているみたい?

「BLACK *-POINTING SMALL TRIANGLE▴」と「WHITE *-POINTING SMALL TRIANGLE▵」は単純に小さいというだけ?

「BLACK MEDIUM *-POINTING TRIANGLE CENTRED⯅」は「BLACK MEDIUM *-POINTING TRIANGLE」と何が違うのか。単に中くらいのが欲しいならコレ?

「BLACK *-POINTING ISOSCELES RIGHT TRIANGLE🞁」は直角二等辺三角形です。最近はこれをorg-modeの見出しのマークとして使っています。開閉で見た目を変化させているので、閉じているときは🞂で開いているときは🞃にしています。

基本的なものだけでもこれだけあるわけですが、イマイチ使い分けがよく分かりません。子どもの頃漢字成り立ち辞典というのを持っていましたが、Unicode成り立ち辞典が欲しい。

他にも探せば三角形っぽいものは沢山あるみたいです。「BLACK * POINTING POINTER►」や「WHITE * POINTING POINTER▻」なんかは比較的上に挙げたものと同列に扱えそうです(2方向だけですが)。一方で三角形の形をした文字というのも多数あって、典型的なのはデルタΔですが、ここまでくるとどこまで「三角形」と呼ぶのかを考える必要があるでしょう。

それで、なんでこんなことをいきなり調べ始めたかというと、私のEmacsではこの辺りの記号が正しく表示できておらず、普段使っているフォントをFontForgeでいじって字形を調整している最中だからです。

2025-03-12 ,

Android版Emacsのためにした設定

先日Android版のEmacsを導入してみましたが、その後にした設定をまとめました。

これまでの設定:

以下はそれ以外の細かい設定です。

起動したらorgファイルを開く

私はこれまでEmacsで起動画面のカスタマイズなどは特にしていませんでした。とは言ってもデフォルトのスプラッシュスクリーンくらいはOFFにしていたので(つまり (setq inhibit-splash-screen t))、起動したらscratchバッファが表示される状態でした。

PCならこれで全く困らずそこから必要に応じてファイルやディレクトリを開けば済むわけですが、Androidスマホだと何を開くにも小さなボタンを何回も押さなければならず苦痛です(メニューバーの中のブックマークの位置と来たら……)。

なのでとりあえずデフォルトのorgファイルを最初から開くことにしました。その名も phone.org

とは言ってもやることは init.el の最後でfind-fileするだけです。一応OSとファイルの存在くらいは確認しておきましょうか。

(when (eq system-type 'android)
  (let ((home-file "~/my-org-files/phone.org"))
    (when (file-regular-p home-file)
      (find-file home-file))))

org-modeでメニューを作る

そうして開いたorgファイルにはよくアクセスするファイルやディレクトリへのリンクを書いておくわけです。

しかしそれだけでは足りません。よく使うコマンドもタッチで実行できるようにしておきたいところ。そんな時に便利なのが elisp: リンクです。 [[elisp:(message "Hello")][ハロー]] などと書けば押すとEmacs Lispの式が評価されるリンクが作成できます。description(ハローの部分)には画像を指定したりも出来るので工夫次第で綺麗な画面を作ることも出来ることでしょう。他人からもらったorgファイルだと何をされるか分からないので危険なリンクですが、自分が作ったものなら何の問題もありません。一応評価するかyes/noで聞かれるのでキーボードからだとy・e・s・RETと4ストローク必要ですが、Androidであればダイアログのyesをタップするだけです。むしろちょうど良いくらいです。

例えば次のような感じです(適当に似たようなものをでっち上げたので動作未確認)。

#+TITLE: Android用ホームファイル

- [[elisp:(my-ssh-setup)][ssh-agentの起動]]

- [[file:/data/data/com.termux/files/home/][Termux Home]]

- [[file:/data/data/org.gnu.emacs/files/][Emacs Home]]

- [[file:/sdcard/][SDCARD]]

- [[file:~/my-org-files/][Orgファイルたち]]
  - [[elisp:(my-git-pull "~/my-org-files/")][(my-git-pull)]]
  - [[elisp:(my-git-commit-push "~/my-org-files/")][(my-git-commit-push)]]
  - [[file:~/my-org-files/phone.org][phone.org]]
  - [[file:~/my-org-files/todo.org][todo.org]]
  - [[elisp:(org-agenda nil "a")][Agenda]]

- [[file:~/my-emacs-config/][Emacsの設定ファイル]]
  - [[elisp:(my-git-pull "~/my-emacs-config/")][(my-git-pull)]]
  - [[elisp:(magit-status "~/my-emacs-config/")][(magit-status)]]
  - [[file:~/my-emacs-config/init.el][init.el]]
  - [[file:~/my-emacs-config/early-init.el][early-init.el]]

各種ディレクトリへのリンクの他に、Gitのpushやpull、org-modeのアジェンダの表示なんかを入れておくと良いでしょう。これでAndroidからいつでも予定を確認できます。

Gitのためにあらかじめ次のような関数を用意しておきます。

(defun my-git-pull (dir)
  (let ((default-directory dir))
    (vc-pull)))

(defun my-git-commit-push (dir)
  (let ((default-directory dir))
    (unless
        (and (zerop (shell-command "git commit . -m \"Update\"" "*my-git-push*"))
             (zerop (shell-command "git push" "*my-git-push*")))
      (pop-to-buffer "*my-git-push*"))))

~/my-org-files/ の下は同期ソフト代わりにGitを使うようなイメージです。将来的には変更がぶつかったらある程度自動的にマージ(rebase?)するような仕掛けも欲しい所。

~/my-emacs-config/ の下はある程度ちゃんとコミットメッセージを書くためにとりあえずMagitを起動するリンクを載せておきましたが、Magitはなぜかものすごく遅いのでvc-checkinとvc-pushの方がいいかもしれません。

リンクを開くときに新しいウィンドウを開かない

使っていると何かにつけて新しいウィンドウを開いてくるのが気になります。PCでは分割ウィンドウが不要ならC-x 1を押せば済む話ですが、タッチ操作がメインの場合はウィンドウを閉じるのにも一手間必要です。画面が小さいので分割されると見づらくなってしまうということも関係しているのでしょう。

先日の設定(モードラインをドラッグしてウィンドウを消す)でウィンドウを簡単に閉じられるようになったとはいえ、そもそも最初から新しいウィンドウを開かなければいい話です。とは言え別ウィンドウを開くのが必ず悪いかと言われればよく分からないので、とりあえず気になったところだけ直すことにします。display-buffer-alistあたりを変更しようかとも思いましたが、とりあえず個々のコマンドの設定を変更してみます。

;; org-modeのリンクを開くときに別ウィンドウを開かない
(setf (alist-get 'file org-link-frame-setup) 'find-file)

;; org-agendaで別ウィンドウを開かない
(setq org-agenda-window-setup 'current-window)

;; Dired内でファイルをタップしたときに別ウィンドウを開かない
(define-key dired-mode-map [mouse-2] #'dired-mouse-find-file)

;; Diredから他のディレクトリを開くときに元のDiredバッファをkillする
;; (`dired-mouse-find-file'の挙動に影響する。
;;   dired-mouse-find-alter-fileは存在しない)
(setq dired-kill-when-opening-new-dired-buffer t)

おそらく他にも同じように感じる場所があると思いますが、気がついたら逐一設定していくことにします。

org-captureの保存先

ディレクトリ構成が変わったのでorg-captureの保存先も変える必要があります。……と思ったのですが、その後 ~/ とシンボリックリンクを組み合わせてPCと同じパスになるようにしてしまったので設定は不要になりました。

ファイルの自動同期

ファイルの同期自体は上に書いたとおりGitを同期ソフト代わりに使うことで実現しています。

その上でPCではファイルの保存やEmacsの終了のタイミングでファイルを同期するような仕組みを整備していましたがスマホでは止めておきました。電波が入らないところで使っている可能性があるので。とりあえず手動で同期しようと思います。

物理キーボードからのIMEのON/OFF

Bluetoothのハードウェアキーボードを接続してみたのですが、IMEのON/OFFの方法がよく分かりませんでした。半角/全角を押すと切り替わるように見えてOFFなのにM-<やM->を押すと<や>が入力されてしまったり、かと思えばそれらはコマンドとして認識されるけど半角/全角切り替えはできなかったり。

調べてみると、これはどうもtext-conversion-styleoverriding-text-conversion-styleが影響しているようです。

text-conversion-styleはIMEの挙動を指定するバッファローカル変数です。nilのときIME無効、非nilのとき有効になるようです。非nilの中でも、シンボル action やシンボル password といった指定もあります。ザッと検索してみたところ、text-mode(org-modeはここに含まれます)やprog-mode(一般的なプログラミング用のモードはここに含まれます)では tcomint-mode(shell-mode等がここに含まれます)やminibuffer-modeでは actionread-passwdでは password が指定されているようでした。つまりバッファ毎にそのバッファに入力される文字の種類・性質を指定するという側面があるようです。

それでtext-conversion-styleがnilのバッファではIMEを介さない入力が可能で、tのバッファでは常にIMEを介した入力になってしまうということのようです。極端だってば。(注: この辺りの挙動はIMEによっても若干異なるようです。Gboardはtext-conversion-styleがnilの時でもGboardのショートカットキーを完全に無効にすることはできませんでした。M->を押すと何か不可解なエラーが出たり、M-tを押すとGoogle翻訳を使おうとしたりします。ATOK Passport Proはショートカットキーが干渉することはありませんでしたが、text-conversion-styleがtのときは半角文字もIME経由でバッファへ直接挿入されるためorg-modeのスピードコマンドやedebugのアルファベット1文字のキーが使えなかったりしました)

text-conversion-styleの効果はグローバル変数overriding-text-conversion-styleで上書きできます。デフォルトの値はシンボル lambdatext-conversion-styleの値を尊重します。それ以外の値が指定されている場合は、text-conversion-styleは無視してoverriding-text-conversion-styleの値が使われるようです。

ということはこのoverriding-text-conversion-styleを切り替えるコマンドを作ればIMEの挙動をユーザーが明示的に指定出来るようになるはずです。

(defun my-toggle-text-conversion-style ()
  "`overriding-text-conversion-style'を`lambda'とnilとの間で切り替えます。"
  (interactive)
  (cond
   ((eq overriding-text-conversion-style 'lambda)
    (setq overriding-text-conversion-style nil)
    (set-text-conversion-style text-conversion-style))
   ((null overriding-text-conversion-style)
    (setq overriding-text-conversion-style 'lambda)
    (set-text-conversion-style text-conversion-style))))

それを私はCtrl+変換に割り当てました。私は普段PCでもこのキーでIMEのON/OFFを切り替えているので。

(define-key global-map [C-henkan] #'my-toggle-text-conversion-style)

半角/全角は英数/カナ切り替え(カナロック)のようなものだと考えることにします。

ちなみにハードウェアキーボードの細かいレイアウトはshiftrot/caps2ctrlkcmファイルを独自にカスタマイズして調整しています。

line-spacingや文字サイズの調整

指で位置を指定することを考えるとline-spacingは大きめが良いでしょうね。使うフォントにもよると思いますが。文字サイズは視力との兼ね合いでしょうか。

タッチによるスクロールをピクセル単位にする(2025-07-22追記)

(setq touch-screen-precision-scroll t)

おしまい

これでAndroid版のEmacsを触って最初に思いついた設定は一通り終わりました。

最初はAndroid版のEmacsなんて使い物になるの~? と疑っていましたが、思っていた以上に使える道具だということが分かってきました。Orgzlyはもう私には必要ありません。Android用に作られた昔からあるテキストエディタアプリも用済みです。タッチ操作で普通に編集できるので遜色ありません。長押しメニューが圧倒的に便利です。Dropbox等のクラウドストレージとの連携が必要な人だとまだその手の機能を搭載したアプリの方に分があるかもしれません……ってTrampのrcloneメソッドがあるんですね(使ったことはありません)。

すごいのはほぼ全ての設定がPCと同一だということです。Androidのためだけに設定したことは、これまでに紹介したものしかありません。

もちろん今後も触っていて思いついた改善をしていこうと思います。決して尽きることはないでしょう。