Skip to content

noctuid/framegroups.el

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 

Repository files navigation

Motivation

Emacs already has more workspace management packages than I can count. In the past, I exclusively used workgroups2 because no other package had all of the functionality I consider important. These are the main features supported by this package:

  • Workspace-local/independent window layout history/undo (this is the feature that no package I am aware of besides workgroups2 supports and is the primary reason I created this package)
  • Named, not (just) numbered, workspaces; each represent some context (e.g. book notes, programming project, etc.)
  • Hooks for creating and switching between workspaces to allow default session setup and so that workspace-specific keybindings/settings can be applied
  • No limit to the number of workspaces (elscreen is the main and possibly only offender)
  • Persistence (this is generally not an issue as desktop.el can be used to store sessions)

While workgroups2 has all of the above functionality, it has serious issues as well:

  • It is unmaintained and cannot save layouts on the latest Emacs
  • Extra configuration is necessary to fix some annoyances:
    • Golden ratio/zoom must be temporarily disabled when switching workgroups to prevent “window too small for splitting” errors when switching to a workgroup with more than two or three windows (this is also an issue for some alternative packages I’ve tried)
    • Window resizing (e.g. automatically done by golden ratio/zoom) is recorded by default (moving through window size history is pretty useless, especially if you are using a package like golden ratio; winner-mode, on the other hand, doesn’t record window resizing)

Workgroups2 is a fairly large package, and I don’t want to attempt to maintain/fix it when I only need some of its functionality. The goal of this package is to be as simple as alternatives while also allowing for the useful functionality they lack. The main principle is to use multiple frames as workspaces instead switching between window layouts inside a single frame. This has several advantages:

  • winner-mode keeps track of window configuration changes separately for each frame
  • desktop.el can be used to persist and restore all frames
  • The user can change keybindings based on the frame’s fg-name property (e.g. by using fg-after-switch-hook) and make use of frame-local variables
  • No “window too small for splitting” issues

Requirements and Limitations

The primary issue with using frames for workgroups-like functionality is that emacs (by itself) has no way of displaying only one frame at a time and hiding the others. This isn’t an issue if you are willing to dedicate different desktops to different frames. On the other hand, frames aren’t great if you want a lot of contexts/groups or would prefer to use keys bound in emacs for switching between them instead of always using global hotkeys (since select-frame-set-input-focus does not work across desktops).

By default, this package attempts address these issues issue by using xdotool to hide the old frame and switch to a new one. If xelb is able to easily map/unmap windows, I may switch to using it in the future.

Note that this package will only attempt to use xdotool if the user is using X11, xdotool is installed, and the related settings (see below) are non-nil. Otherwise, it will not attempt to hide frames and will use emacs’ builtin frame switching function.

To clarify, if you are not using Linux/X11 or do not wish to use xdotool, most of this package’s functionality is still available:

  • fg-rename-frame works
  • fg-create-frame works
  • fg-create-hook works
  • the mode line indicator works
  • fg-switch-to-frame and fg-switch-to-last-frame will likely not work across desktops (you’ll need to use global WM keys to switch between desktops); they should work for frames located on the same desktop
  • fg-after-switch-hook is only triggered from fg-switch-to-(last-)frame (you can use focus-in-hook instead if you want to dedicate different WM desktops to different frames)
  • fg-desktop-setup will not work

In summary, reliable frame switching when using multiple desktops requires xdotool (basically a hack). Using xdotool to hide/switch windows can also cause flicker especially if you are not using a compositor. That said, if all your frames are on a single desktop, using xdotool is not necessary. You can instead have only one frame visible by using the monocle/fullscreen layout for that desktop.

Terminology

The following sections use the term “framegroup” to refer to a frame that this package is managing (i.e. it was named/focused using this package’s commands).

Settings/Hooks

  • fg-hide-with-xdotool (default t): whether to hide the old frame when switching to a new one (only has an effect when using X11 and xdotool is installed)
  • fg-switch-with-xdotool (default t): whether to switch frames using xdotool instead of emacs’ select-frame-set-input-focus (only has an effect when using X11 and xdotool is installed and fg-hide-with-xdotool is nil; when fg-hide-with-xdotool is non-nil, xdotool must be used to remap hidden windows)
  • fg-auto-create (default t): whether to automatically create a framegroup when attempting to switch to a non-existent one
  • fg-create-hook: hook run when a framegroup is first created (renaming also triggers this hook)
  • fg-after-switch-hook: hook run after switching framegroups; functions are run with the name of the new framegroup as an argument

Provided Functions/Macros

Commands:

  • fg-rename-frame: set the name of the current framegroup
  • fg-create-frame: choose a name and create a new framegroup
  • fg-switch-to-frame: switch to another framegroup
  • fg-switch-to-last-frame: switch to the previously focused framegroup

Helper functions/macros:

  • fg-switch: a convenience macro that returns a function that will switch to a framegroup (e.g. (define-key "key" (fg-switch "notes")))
  • fg-desktop-setup: will add to desktop-read-hook to hide all but the last focused frame when restoring a session
  • fg-mode-line-string: return the current framegroup name formatted for the mode line

This package does not provide numbered workspaces or directional switching commands as I personally prefer to have named contexts. You could of course simply name your frames with numbers and create directional switching keybindings yourself with fg-switch, but I do not plan to this functionality directly.

Example Configuration

;; enable `winner-mode' for undo
(winner-mode)
(global-set-key "C-c l" 'winner-undo)
(global-set-key "C-c L" 'winner-redo)

;; enable `desktop-save-mode' for persistence
;; NOTE: It seems Emacs occasionally hangs when restoring a lot of frames with
;; desktop.el
(fg-desktop-setup)
(desktop-save-mode)

;; binding keys to switch to specific framegroups
(global-set-key "C-c e" (fg-switch "emacs"))
(global-set-key "C-c p" (fg-switch "prog"))
;; ...

;; default layouts for framegroups
(defun my-framegroup-setup (name &rest _)
  "Set up default framegroup layouts."
  (interactive)
  (pcase name
    ;; emacs configuration
    ("emacs"
     (find-file "~/.emacs.d/other.el")
     (split-window-right)
     (find-file "~/.emacs.d/init.el"))
    ;; programming projects
    ("prog"
     (find-file "~/src"))
    ;; dotfiles
    ("config"
     (find-file "~/dotfiles"))
    ("mail"
     (mu4e))
    ("music"
     (mingus))))

(add-hook 'fg-create-hook #'my-framegroup-setup)

;; binding keys for the current framegroup
(defmacro my-ff (file)
  "Wrapper for creating `find-file' commands."
  `(lambda () (interactive) (find-file ,file)))

(defun my-framegroup-keybindings (name &rest _)
  (pcase name
    ("emacs"
     (global-set-key "C-c , i" (my-ff "~/.emacs.d/init.el")))
    ("prog"
     (global-set-key "C-c , r" (my-ff "README.org"))
     (global-set-key "C-c , d" #'projectile-edit-dir-locals))))

(add-hook 'fg-after-switch-hook #'my-framegroup-keybindings)

For mode line integration, you can insert fg-mode-line-string into the mode line:

(setq-default mode-line-format
              ;; ...
              '(:eval (when (fboundp 'fg-mode-line-string)
                        (fg-mode-line-string)))
              ;; ...
              )

About

Workspaces for emacs using frames

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published