Minor Mode Example (StumpWM Manual)

### 10.3 Minor Mode Example ¶

A simple example of a minor mode is a version of the Emacs modes viper or evil for StumpWM. Such a minor mode might look like this:

    (define-minor-mode swm-evil-mode () ()
      (:scope :screen)
      (:interactive t)
      (:top-map '(("i" . "swm-evil-mode")
                  ("j" . "move-focus down")
                  ("k" . "move-focus up")
                  ("h" . "move-focus left")
                  ("l" . "move-focus right")
                  ("p" . "pull-hidden-previous")
                  ("n" . "pull-hidden-next")
                  ("S" . "hsplit")
                  ("s" . "vsplit")
                  ("r" . "remove-split")
                  ("g" . *groups-map*)
                  ("x" . *exchange-window-map*)))
      (:lighter-make-clickable nil)
      (:lighter "EVIL"))

In the above example, the minor mode `swm-evil-mode` is defined, alongside a command of the same name which toggles it on and off. The minor mode is scoped to a screen, meaning that upon activation it will be dynamically mixed in to the screen object. The lighter is the string `"EVIL"` and the lighter is not made clickable.

When defining a minor mode top map it is important to avoid multi-key bindings that clobber the prefix key. For example, if the prefix key is `C-t` then defining the keybinding `C-t n` in the top map of a minor mode is an error. Instead bind the key `n` in the minor mode’s root map.

As another example we can define a frame topbar mode. This should adjust every frame to leave extra space at the top of the frame to display a bar of some sort. The following assumes that the functions `frame-display-height` and `frame-display-y` are generic.

    (defclass frame-topbar ()
      ((frame-topbar-height :initform 10 :accessor frame-topbar-height)))

    (defmethod frame-display-height :around (group (frame frame-topbar))
      (let ((height (call-next-method)))
        (- height (frame-topbar-height frame))))

    (defmethod frame-display-y :around (group (frame frame-topbar))
      (let ((y (call-next-method)))
        (+ y (frame-topbar-height frame))))

    (define-minor-mode frame-bar (frame-topbar minor-mode) ()
      (:global t)
      (:scope :frame)
      (:lighter "T-BAR")
      (:interactive frame-topbar-mode))

    (defmethod update-instance-for-different-class :after
        (prev (obj frame-bar) &rest rest)
      (declare (ignore prev rest))
      (when (frame-window obj)
        (let* ((group (window-group (frame-window obj)))
               (windows (frame-windows group obj)))
          (mapc #'maximize-window windows))))

In the above example, a class is defined which holds the height of the frame topbar. Then two around methods are defined such that windows querying the frame for their y position and height get an updated value reflecting the topbars presence. Then a minor mode is defined which inherits from the class we defined. It is scoped to frames and is a global minor mode, so it will be enabled in all existing frames and any other frames as they are created. Finally the initialization is handled in the after method for update-instance-for-different-class, which updates every window to have a new size which respects the topbar.

The implementation of the actual topbar is left as an exercise for the reader.