10 Minor Modes

Like Emacs, StumpWM has the concept of minor modes. These are defined by the macro DEFINE-MINOR-MODE. Defining a minor mode creates a class and a set of methods specializing upon it. Minor modes are scoped to a window, head, group, or screen, or they may be unscoped. In addition to this minor modes may be local or global. When a minor mode is global all new instances of the scope object will be created with the minor mode already active in them. Minor modes define their own top level and root level keymaps, as well as hooks that are run upon enabling or disabling the minor mode, and a lighter to display in the mode line.

Minor modes are mixins that get added to the appropriate scope object when enabled. As such minor modes allow the augmenting, modifying, and overriding of default StumpWM behavior by defining methods for the generic functions of the scope object. For example, a minor mode may be scoped to a window and define a method for the generic function UPDATE-DECORATION to change how window decoration is handled for the windows it is enabled in.

Macro: define-minor-mode mode superclasses slots &rest options

Define a minor mode as a class to be instantiated when the minor mode is activated. Minor modes are dynamically mixed in to and out of the appropriate object when they are enabled or disabled.

If SUPERCLASSES is not provided a default superclass of MINOR-MODE will be provided. OPTIONS may include all normal options when defining a class, with the addition of the following options:

  • (:SCOPE SCOPE-DESIGNATOR)
    The :SCOPE option determines what object(s) the minor mode can be mixed in with. New scopes can be defined with the macro DEFINE-MINOR-MODE-SCOPE.
  • (:GLOBAL (OR T NIL))
    When true the :GLOBAL option changes the way enable methods are defined to track the minor mode and autoenable it in all existing scope objects, as well as autoenabled when new scope objects are instantiated. If the :SCOPE option is :UNSCOPED then this option does not need to be provided.
  • (:TOP-MAP spec)
    The minor modes top map is created based upon the provided spec, which must be a list of cons cells whose car is a key sequence and whose cdr is a binding. For example: (list (cons "C-m x" "echo")). This would bind the key sequence C-m x to the echo command. A reference to this keymap is stored as a slot in the minor mode object and can be accessed via the reader MODE-KEYMAP where MODE is the minor mode name.
  • (:ROOT-MAP spec)
    The minor modes root map is created based upon the provided spec. The spec is as described in the :TOP-MAP option.
  • (:EXPOSE-KEYMAPS (OR T NIL))
    This value is used at macroexpansion time to determine whether or not to generate keymap variables or store the keymap within the object. When T the variables *MODE-TOP-MAP* and *MODE-ROOT-MAP* will be generated.
  • (:REBIND (MEMBER :TOP-MAP :ROOT-MAP :ALL-MAPS))

    This option controls rebinding of the top and root maps. When it is :TOP-MAP the top map is rebound, when it is :ROOT-MAP the root map is rebound, and when it is :ALL-MAPS both the top and root map are rebound. Any rebound map will be rebound to the provided keymap specification. This only has an effect if the minor mode has previously been defined.

  • (:LIGHTER T)
    The :LIGHTER option will be used to generate a function returning a string to display in the mode line. When :LIGHTER is NULL a string is generated based upon the mode name. When it is a string that string is used as is. Otherwise :LIGHTER will assumed to be funcallable and used as is. When it is a symbol or a list that doesn’t begin with LAMBDA or FUNCTION a warning is issued that DEFINE-MINOR-MODE is assuming it is funcallable. When assumed to be funcallable, it is called with the mode object as its only argument.
  • (:LIGHTER-MAKE-CLICKABLE (OR T NIL))
    When :LIGHTER-MAKE-CLICKABLE is T then the :LIGHTER is wrapped in a call to FORMAT-WITH-ON-CLICK-ID, called with the id :ML-ON-CLICK-MINOR-MODE and the mode as a quoted symbol.
  • (:LIGHTER-ON-CLICK FUNCTION)
    When :LIGHTER-ON-CLICK is provided it must be a function of arity one, which will be called whenever the minor modes lighter is clicked, with the button code of the click as its only argument. If this is provided then :LIGHTER-MAKE-CLICKABLE is implied to be T.
  • (:INTERACTIVE (OR SYMBOL T NIL))
    The :INTERACTIVE option determines whether a command to toggle the minor mode on and off is generated. If it is T then a command with the same name as the minor mode is generated. If it is a symbol then that symbol will be used when defining the command.
  • (:ENABLE-WHEN (MODE OBJECT) &BODY BODY)
    When provided, the :ENABLE-WHEN option generates a method for the enable-when generic function. MODE is bound to the mode symbol, and OBJECT is bound to the scope object. If this is not provided, a method is generated which returns T for the minor mode and its scope. If it is provided and is nil, then no method is generated and a method for ENABLE-WHEN which dispatches upon the mode as a symbol and the scope type for the minor mode must be manually defined.
  • (:MAKE-HOOKS (OR T NIL))
    When :MAKE-HOOKS is T a set of hook variables are generated. These variables are fourfold: *MODE-HOOK* is run after explicitly enabling the minor mode. *MODE-ENABLE-HOOK* is run when the minor mode is autoenabled. *MODE-DISABLE-HOOK* is run when the minor mode is autodisabled. Finally *MODE-DESTROY-HOOK* is run when the minor mode is explicitly disabled.
  • (:DEFINE-COMMAND-DEFINER (OR T NIL))
    When :DEFINE-COMMAND-DEFINER is T a macro is defined for defining commands that are active only when the minor mode is active. Commands defined with this macro have the special variable *MINOR-MODE* bound to the minor mode object in their body. The generated macro is called DEFINE-MODE-COMMAND. This option defaults to T.
  • (:MIX-BEFORE &REST RULES)
    The :MIX-BEFORE option defines rules on the order this class should be mixed in relative to other minor modes. This allows the implementer of a minor mode to make the mixing process aware of dependencies that dont otherwise make sense as a class hierarchy; If minor modes FOO and BAR both define around methods for the same method, but FOO’s method must be called first, FOO can add a rule stating that it must come before BAR in the mixin list. RULES must be a set of conses which have the form (SYMBOL-DESIGNATOR . PACKAGE-DESIGNATOR). SYMBOL-DESIGNATOR must be a valid argument to #’STRING, and PACKAGE-DESIGNATOR must be a valid argument to #’FIND-PACKAGE. Together these shall form a single symbol which should be the class name of the minor mode being referred to by the rule.
  • (:MIX-AFTER &REST RULES)
    The :MIX-AFTER option is similar to the :MIX-BEFORE option, except it specifies classes that this minor mode should occur after in the mixin list.

Example:

(define-minor-mode evil-mode () ()
  (:scope :unscoped)
  (:top-map '(("j" . "move-focus down")
              ("k" . "move-focus up")
              ("h" . "move-focus left")
              ("l" . "move-focus right")
              ("x" . *exchange-window-map*)
              ("C-m b" . "evil-echo")))
  (:lighter "EVIL")
  (:lighter-make-clickable nil))

(define-evil-mode-command evil-echo () ()
  (run-commands "echo"))