I composed a short video about using spatial groups in StumpWM to share. I've tried to explain the idea of spatial grouping before on IRC, and I felt a demonstration would be clearer.
http://demosthenes.org/tmp/StumpWMSpatialGroups.mp4 Please pardon the rough edges. I'm not starting a Youtube channel, just trying to have a conversation about how to manage windows and groups in Stump. Spatial grouping abuses Stump's ability to dynamically create groups to create a sparse 3d array of groups, visualized as multiple desktops of a 2d grid of screens. Hotkeys use arrows to navigate the grid and swap desktops in a way which emulates a larger physical screen surface. Using this method allows me to group windows by project, adjacent to each other. This allows absolute navigation to each window, as opposed to relative navigation (ie: alt-tab). I'm not grouping related application windows on the same screen, but rather organizing patterns of groups to keep related application windows cognitively near each other. I still may use splits and tile windows within a screen, but not as often. I'm hoping to inspire further discussion on the matter. Critically, I have not tested multiple monitor support. Concepts: Spatial groups use a 3d coordinate system: “x,y,z” Valid groups are “0,0,0”, “-1,2,0”, etc. Z is used as the desktop index Keys: Control-Shift-{Left,Right}: Change desktop (Z axis) Control-Arrows: Navigate virtual screens on current desktop Control-Shift-Up: Return to 0,0 on current desktop (Z) Shift-Arrows: Navigate splits on current screen group Relevant code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (ql:quickload 'cl-ppcre) (in-package :stumpwm) ;; change the prefix key to something else (set-prefix-key (kbd "s-.")) ;; fix scroll wheel? (setf (getenv "GDK_CORE_DEVICE_EVENTS") "1") ;; early define global vars (defparameter *z-cursors* '() "Association list of groups by Z coordinate for preserving last position on that plane.") (defparameter *last-cursor* NIL "Previous group for popping back.") ;; Change focus (set-focus-color "darkblue") ;; Set the border around the current frame (setf *suppress-frame-indicator* t) ;; Stop the popup on the current frame ;; Modeline settings (enable-mode-line (current-screen) (current-head) t) (setf *mode-line-timeout* 60) (setf *screen-mode-line-format* (list "[^B%n^b] %d |" "| %W")) ;; Shift arrows between adjacent windows (define-key *top-map* (kbd "S-Up") "move-focus up") (define-key *top-map* (kbd "S-Down") "move-focus down") (define-key *top-map* (kbd "S-Left") "move-focus left") (define-key *top-map* (kbd "S-Right") "move-focus right") ;; Control arrows between groups (defcommand coord-left () () (coord-group-change -1 0 0)) (defcommand coord-right () () (coord-group-change 1 0 0)) (defcommand coord-up () () (coord-group-change 0 1 0)) (defcommand coord-down () () (coord-group-change 0 -1 0)) (defcommand coord-taskleft () () (coord-group-change 0 0 -1)) (defcommand coord-taskright () () (coord-group-change 0 0 1)) ;; return to 0,0 on the current taskplane as a shortcut to return to the core task (defcommand coord-taskorigin () () (gselect (group-name (my-find-group (current-screen) (format nil "~{~a~^,~}" (list 0 0 (parse-integer (third (cl-ppcre:split "," (group-name (current-group))))))))))) ;; pop back to last location (defcommand coord-taskpop () () (when *last-cursor* (my-gselect *last-cursor*))) (define-key *top-map* (kbd "C-Left") "coord-left") (define-key *top-map* (kbd "C-Right") "coord-right") (define-key *top-map* (kbd "C-Up") "coord-up") (define-key *top-map* (kbd "C-Down") "coord-down") (define-key *top-map* (kbd "C-S-Left") "coord-taskleft") (define-key *top-map* (kbd "C-S-Right") "coord-taskright") (define-key *top-map* (kbd "C-S-Up") "coord-taskorigin") (define-key *top-map* (kbd "C-S-Down") "coord-taskpop") (define-key *top-map* (kbd "s-S-SPC") "fullscreen") ;; Groups will manage the coordinate system ;; format: 0,0,0 with positive and negative numbers ;; create groups on the fly as needed ;; only supports one screen atm, would like multimonitor support later (gnew "0,0,0") ; create origin on startup (defun my-find-group (screen name) (find name (screen-groups screen) :key 'group-name :test 'string=)) (defun my-gselect (name) "Preserve prior location for pop, and handle when group is new" (banish) (setf *last-cursor* (group-name (current-group))) (gselect (group-name (or (my-find-group (current-screen) name) (gnew name))))) (defun coord-group-change (xo yo zo) "Navigate a 3d array of groups using x,y,z coordinates by passing the offset of the change." (let* ((current-coords (mapcar #'parse-integer (cl-ppcre:split "," (group-name (current-group))))) (new-coords (mapcar #'+ current-coords (list xo yo zo))) (new-group-name (format nil "~{~a~^,~}" new-coords)) ) (if (= 0 zo) ;; Not changing taskplanes, so just move by coordinates (my-gselect new-group-name) ;; Changing Z across taskplanes, RESTORE z-cursor for that plane (let ((old-z (third current-coords)) (new-z (third new-coords))) (if (assoc old-z *z-cursors*) (setf (cdr (assoc old-z *z-cursors*)) (current-group)) (push (cons old-z (current-group)) *z-cursors*)) (let ((z-cursor (cdr (or (assoc new-z *z-cursors*) '(nil . nil))))) (if z-cursor ;; Restore saved location (my-gselect (group-name z-cursor)) ;; create new taskplane (my-gselect new-group-name))))))) (defun my-startup () (my-gselect "0,0,0")) (when *initializing* (my-startup)) ------------------------------------------------------------------ Russell Adams rlad...@adamsinfoserv.com PGP Key ID: 0x1160DCB3 http://www.adamsinfoserv.com/ Fingerprint: 1723 D8CA 4280 1EC9 557F 66E8 1154 E018 1160 DCB3