可点击文本(Clickable text)是指可以通过鼠标点击或键盘命令触发相应操作的文本。 许多主模式使用可点击文本来实现文本超链接,简称链接(links)。
插入和管理链接最简单的方式是使用 button 包,参见 See 按钮。
本节将介绍如何通过文本属性在缓冲区中手动设置可点击文本,
为简洁起见,将可点击文本统称为链接(link)。
实现链接包含三个独立步骤:(1) 鼠标悬停在链接上时提示可点击;
(2) 使 RET 或 mouse-2 在该链接上执行对应操作;
(3) 设置 follow-link 条件,使链接支持 mouse-1-click-follows-link。
要提示可点击状态,需为链接文本添加 mouse-face 文本属性,
Emacs 会在鼠标悬停时高亮该链接。此外,还应通过 help-echo 文本属性
定义提示框或回显区消息。See 具有特殊含义的文本属性。
例如,Dired 用以下方式标记文件名可点击:
(if (dired-move-to-filename)
(add-text-properties
(point)
(save-excursion
(dired-move-to-end-of-filename)
(point))
'(mouse-face highlight
help-echo "mouse-2: visit this file in other window")))
要使链接可点击,需将 RET 与 mouse-2 绑定到执行目标操作的命令。 每个命令应检查是否在链接上调用,并执行相应逻辑。 例如,Dired 主模式按键映射将 mouse-2 绑定到如下命令:
(defun dired-mouse-find-file-other-window (event)
"In Dired, visit the file or directory name you click on."
(interactive "e")
(let ((window (posn-window (event-end event)))
(pos (posn-point (event-end event)))
file)
(if (not (windowp window))
(error "No file chosen"))
(with-current-buffer (window-buffer window)
(goto-char pos)
(setq file (dired-get-file-for-visit)))
(if (file-directory-p file)
(or (and (cdr dired-subdir-alist)
(dired-goto-subdir file))
(progn
(select-window window)
(dired-other-window file)))
(select-window window)
(find-file-other-window (file-name-sans-versions file t)))))
该命令使用函数 posn-window 与 posn-point
确定点击位置,使用 dired-get-file-for-visit 确定要打开的文件。
不必在主模式按键映射中绑定鼠标命令,也可以通过 keymap 文本属性
在链接文本内部绑定(see 具有特殊含义的文本属性)。例如:
(let ((map (make-sparse-keymap))) (define-key map [mouse-2] 'operate-this-button) (put-text-property link-start link-end 'keymap map))
通过该方式可以轻松为不同链接定义不同命令, 同时缓冲区其他文本仍可使用 RET 与 mouse-2 的全局绑定。
Emacs 中点击链接的基础命令为 mouse-2。
为兼容其他图形应用,Emacs 也支持在链接上快速点击 mouse-1(点击时不移动鼠标)触发链接,
该行为由用户选项 mouse-1-click-follows-link 控制。
See Mouse References in The GNU Emacs Manual。
要使链接支持 mouse-1-click-follows-link,
必须满足以下任一条件:(1) 为链接文本添加 follow-link 文本或覆盖层属性;
或 (2) 将 follow-link 事件绑定到按键映射(可以是主模式按键映射,
或通过 keymap 文本属性指定的局部映射)。
follow-link 属性的值或事件绑定结果,作为链接操作的判定条件。
该条件向 Emacs 说明两点:何种情况下 mouse-1 点击应视为在链接内,
以及如何计算动作码以决定 mouse-1 点击的转换目标。
链接操作条件可以是以下之一:
mouse-face若条件为符号 mouse-face,则当某位置存在非 nil 的 mouse-face 属性时,
视为位于链接内。动作码始终为 t。
例如,Info 模式处理 mouse-1 的方式:
(keymap-set Info-mode-map "<follow-link>" 'mouse-face)
若条件为函数 func,则当 (func pos) 求值非 nil 时,
位置 pos 视为在链接内。func 的返回值作为动作码。
例如,pcvs 模式仅允许 mouse-1 在文件名上触发链接:
(keymap-set map "<follow-link>"
(lambda (pos)
(eq (get-char-property pos 'face) 'cvs-filename-face)))
若条件值为其他类型,则该位置始终视为在链接内,条件本身即为动作码。 显然,此类条件仅应通过链接文本的文本属性或覆盖层属性设置 (避免作用于整个缓冲区)。
动作码决定 mouse-1 如何触发链接:
若动作码为字符串或向量,mouse-1 事件会转换为字符串或向量的第一个元素;
即 mouse-1 的操作对应该字符或符号的局部/全局绑定。
例如,动作码为 "foo" 时,mouse-1 转换为 f;
为 [foo] 时,转换为 foo。
对于其他非 nil 的动作码,mouse-1 事件会转换为
同一位置的 mouse-2 事件。
要为 define-button-type 定义的按钮启用 mouse-1 激活功能,
需为按钮设置 follow-link 属性,属性值为前述链接操作条件。
See 按钮。例如,帮助模式处理 mouse-1 的方式:
(define-button-type 'help-xref 'follow-link t 'action #'help-button-action)
要为 define-widget 定义的控件设置 mouse-1,
需为控件添加 :follow-link 属性,属性值为前述链接操作条件。
例如,link 控件将 mouse-1 点击转换为 RET:
(define-widget 'link 'item "An embedded link." :button-prefix 'widget-link-prefix :button-suffix 'widget-link-suffix :follow-link "\C-m" :help-echo "Follow the link." :format "%[%t%]")