如何根据文本高度自动添加/删除滚动条

时间:2014-01-16 23:32:06

标签: emacs scrollbar elisp

许多商业文字处理器都有默认行为,当文档中的文本行小于可见窗口时,该行为会自动隐藏垂直滚动条;并且,当文档中的文本行大于可见窗口时,滚动条会自动出现。 Emacs缺少这种能力,我希望它能成为各种不同模式的默认行为。

当文档的大小(文本行数)增加或减少时,有没有人有一些想法如何动态地自动删除或添加滚动条?

我在考虑将相关线程中的@phils先前的样本编织到行号的函数中:https://stackoverflow.com/a/10593165/2112489

即使我没有使用linum-mode,我也希望它有效。但是,我不认为滚动条函数应该在每个命令之后运行 - 它应该仅在添加或减去新行时运行,同时考虑visual-line-mode(即,包装处于活动状态)可能存在活性

以下片段的灵感来自@ abo-abo之前在相关主题中的回答:https://stackoverflow.com/a/20923695/2112489

(cond
  ((not (> (count-lines (point-min) (point-max)) (window-height)))
    (set-window-scroll-bars (get-buffer-window (buffer-name) (selected-frame)) 0 nil))
  ((and
      (> (count-lines (point-min) (point-max)) (window-height))
      (not (equal (window-scroll-bars) `(15 2 t nil))))
    (set-window-scroll-bars (get-buffer-window (buffer-name) (selected-frame)) 15 'right)))

编辑(2014年1月17日):工作草案基于@Drew对此主题的有用答案。

编辑(2014年1月19日):添加了一个使用vertical-motion统计每个自动换行的函数。在创建初始帧之后,Emacs似乎设置了initial-frame-default,因此滚动条在一瞬间可见 - 为了避免看到这一点,修改初始帧的帧参数似乎修复了这个视觉效果问题。现在使用window-text-height代替window-height - 返回的高度不包括分隔符,模式行,任何标题行,也不包括文本区域底部的任何部分高度行。< / em>在使用linum-modepost-command-hookchange-major-mode-hook方面复制了window-configuration-change-hook使用的方法。添加window-live-p条件以避免在启动Emacs时出现post-command-hook错误,同时各种缓冲区加载时看不到。添加处理narrow-to-region的条件 - 仍然不确定为什么这种情况会导致Emacs在循环中冻结或什么 - 现在需要解决方法。 2014年1月19日的最新版Emacs Trunk似乎解决了以前版本中遇到的视觉显示问题 - 因此,不再需要redraw-frame。将(redisplay t)添加到函数count-vertical-lines,这会在切换缓冲区时加快显示新缓冲区的速度。为缓冲区添加了regexp,它总是有滚动条或从不有滚动条。

编辑(2014年1月20日):只添加了一个主要条件,即有一个实时窗口,并从lawlist-scroll-bar函数的各个分支中删除了相同的条件。为narrow-to-region情况添加了附加条件,以便仅在缩小之前存在滚动条时才需要删除滚动条。

编辑(2014年1月21日):通过此修订,不再需要计算行数(导致大缓冲区减慢)。新方法是基于四(4)个点的简单数学计算,这些点仅在几分之一秒内确定 - 即point-minpoint-maxwindow-start和{{ 1}}。如果window-end移出屏幕,则添加滚动条 - 我认为这种行为是有道理的 - 虽然,我确实停下来思考滚动条是否也应该作为视觉表示是否在无论point-min是否移出窗口,point-minpoint-max的参数实际上都可以放入窗口。这个例子中没有一个钩子能够处理point-min情况(针对同一帧的同一个窗口,两个已经有焦点) - 所以,我创建了自己的display-buffer(这是超出本例范围)。

display-buffer-hook

支持regexp功能:

;;;;;;;;;;;;;;;;;;;;;;;;;; LAWLIST SCROLL BAR MODE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar regexp-always-scroll-bar '("\\.yes" "\\*Scroll-Bar\\*")
  "Regexp matching buffer names that will always have scroll bars.")

(defvar regexp-never-scroll-bar '("\\.off" "\\.not")
  "Regexp matching buffer names that will never have scroll bars.")

(add-to-list 'default-frame-alist '(vertical-scroll-bars . nil))

(modify-all-frames-parameters (list (cons 'vertical-scroll-bars nil)))

(defun lawlist-scroll-bar ()
  (when (window-live-p (get-buffer-window (current-buffer)))
    (redisplay t)
    (cond
      ;; not regexp matches | not narrow-to-region
      ((and
          (not (regexp-match-p regexp-always-scroll-bar (buffer-name)))
          (not (regexp-match-p regexp-never-scroll-bar (buffer-name)))
          (equal (- (point-max) (point-min)) (buffer-size)))
        (cond
          ;; Lines of text are less-than or equal-to window height,
          ;; and scroll bars are present (which need to be removed).
          ((and
              (<= (- (point-max) (point-min)) (- (window-end) (window-start)))
              (equal (window-scroll-bars) `(15 2 right nil)))
            (set-window-scroll-bars (selected-window) 0 'right nil))
          ;; Lines of text are greater-than window height, and
          ;; scroll bars are not present and need to be added.
          ((and
              (> (- (point-max) (point-min)) (- (window-end) (window-start)))
              (not (equal (window-scroll-bars) `(15 2 right nil))))
            (set-window-scroll-bars (selected-window) 15 'right nil))))
      ;; Narrow-to-region is active, and scroll bars are present
      ;; (which need to be removed).
      ((and
          (not (equal (- (point-max) (point-min)) (buffer-size)))
          (equal (window-scroll-bars) `(15 2 right nil)))
        (set-window-scroll-bars (selected-window) 0 'right nil))
      ;; not narrow-to-region | regexp always scroll-bars
      ((and
          (equal (- (point-max) (point-min)) (buffer-size))
          (regexp-match-p regexp-always-scroll-bar (buffer-name)))
        (set-window-scroll-bars (selected-window) 15 'right nil))
      ;; not narrow-to-region | regexp never scroll-bars
      ((and
          (equal (- (point-max) (point-min)) (buffer-size))
          (regexp-match-p regexp-never-scroll-bar (buffer-name)))
        (set-window-scroll-bars (selected-window) 0 'right nil)))))

(define-minor-mode lawlist-scroll-bar-mode
  "This is a custom scroll bar mode."
  :lighter " sc"
  (if lawlist-scroll-bar-mode
    (progn
      (add-hook 'post-command-hook 'lawlist-scroll-bar nil t)
      ;; (add-hook 'change-major-mode-hook 'lawlist-scroll-bar nil t)
      ;; (add-hook 'window-configuration-change-hook 'lawlist-scroll-bar nil t)
       )
    (remove-hook 'post-command-hook 'lawlist-scroll-bar t)
    (remove-hook 'change-major-mode-hook 'lawlist-scroll-bar t)
    (remove-hook 'window-configuration-change-hook 'lawlist-scroll-bar t)))

(define-globalized-minor-mode global-lawlist-scroll-bar-mode
  lawlist-scroll-bar-mode lawlist-scroll-bar-on)

(defun lawlist-scroll-bar-on ()
  (unless (minibufferp)
    (lawlist-scroll-bar-mode 1)))

(global-lawlist-scroll-bar-mode)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

2 个答案:

答案 0 :(得分:3)

目前尚不清楚“动态”是什么意思。为什么不举例说明您正在寻找的行为以及您看到的行为?

如果您只想以交互方式,挂钩或代码方式打开/关闭滚动条,那么scroll-bar-mode应该就是您所需要的。但我只是疯狂地猜测你真正想要的是什么。

您还可以使用menu-bar-no-scroll-barmenu-bar-left-scroll-barmenu-bar-right-scroll-bar。或者只是执行每个命令的操作:(customize-set-variable 'scroll-bar-mode WHATEVER)。或者类似地使用set-scroll-bar-modeset-window-scroll-bars。这取决于你正在寻找的行为。

我推荐M-x apropos scroll-bar。 (或者如果您使用Icicles,只需使用C-h f scroll-bar S-TAB,然后重复C-M-down ...)


更新(在您的评论回复之后)

您可以将其添加到mode-line-position,以便更新模式行自动触发打开/关闭滚动条。这非常有用,例如:

(setq-default
 mode-line-position
 '(:eval
   (progn
     (if (> (count-lines (point-min) (point-max)) (window-height))
         (set-window-scroll-bars nil 20 t)
       (set-window-scroll-bars nil 0 t))
     `((-3 ,(propertize
             "%p"
             'local-map mode-line-column-line-number-mode-map
             'mouse-face 'mode-line-highlight
             'help-echo "Buffer position, mouse-1: Line/col menu"))
       (line-number-mode
        ((column-number-mode
          (10 ,(propertize
                " (%l,%c)"
                'face (and (> (current-column)
                              modelinepos-column-limit)
                           'modelinepos-column-warning)
                'local-map mode-line-column-line-number-mode-map
                'mouse-face 'mode-line-highlight
                'help-echo "Line and column, mouse-1: Line/col menu"))
          (6 ,(propertize
               " L%l"
               'local-map mode-line-column-line-number-mode-map
               'mouse-face 'mode-line-highlight
               'help-echo "Line number, mouse-1: Line/col menu"))))
        ((column-number-mode
          (5 ,(propertize
               " C%c"
               'face (and (> (current-column)
                             modelinepos-column-limit)
                          'modelinepos-column-warning)
               'local-map mode-line-column-line-number-mode-map
               'mouse-face 'mode-line-highlight
               'help-echo "Column number, mouse-1: Line/col menu")))))))))

您也可以使用以下,使用Stefan的suggestion 使用缩放文本,visual-line-mode,图像等使其更好地工作。但是,在这种情况下,只要某些文本在窗口之外由于滚动而滚动,无论该文本是否适合窗口,滚动条都会启动。这是否是一个功能是由您决定。 ; - )

(setq-default
 mode-line-position
 '(:eval
   (let ((scroll-bars  (nth 2 (window-scroll-bars))))
     (if (or (> (point-max) (window-end))  (< (point-min) (window-start)))
         (unless scroll-bars (set-window-scroll-bars nil 20 t))
       (when scroll-bars (set-window-scroll-bars nil 0 t)))
     (unless (equal scroll-bars (nth 2 (window-scroll-bars))) (redraw-frame))
     `((-3 ,(propertize
             "%p"
             'local-map mode-line-column-line-number-mode-map
             'mouse-face 'mode-line-highlight
             'help-echo "Buffer position, mouse-1: Line/col menu"))
       (line-number-mode
        ((column-number-mode
          (10 ,(propertize
                " (%l,%c)"
                'face (and (> (current-column)
                              modelinepos-column-limit)
                           'modelinepos-column-warning)
                'local-map mode-line-column-line-number-mode-map
                'mouse-face 'mode-line-highlight
                'help-echo "Line and column, mouse-1: Line/col menu"))
          (6 ,(propertize
               " L%l"
               'local-map mode-line-column-line-number-mode-map
               'mouse-face 'mode-line-highlight
               'help-echo "Line number, mouse-1: Line/col menu"))))
        ((column-number-mode
          (5 ,(propertize
               " C%c"
               'face (and (> (current-column)
                             modelinepos-column-limit)
                          'modelinepos-column-warning)
               'local-map mode-line-column-line-number-mode-map
               'mouse-face 'mode-line-highlight
               'help-echo "Column number, mouse-1: Line/col menu")))))))))

答案 1 :(得分:2)

如果您习惯只使用滚动条作为您在缓冲区中的位置的可视指示器(而不是查看模式行),您可能会喜欢yascroll之类的包。它使用条纹显示滚动条,如果文档中的文本行小于可见窗口,它就不会显示它。

优点是没有小部件从屏幕上显示真实状态。

缺点是您无法使用鼠标滚动条(有些用户从不这样做,反正)