From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Pranshu Sharma via "Emacs development discussions." Newsgroups: gmane.emacs.devel Subject: Re: Add function to rotate/transpose all windows Date: Fri, 20 Dec 2024 20:11:52 +1000 Message-ID: <87h66yd5p3.fsf@bauherren.ovh> References: <87setpdv21.fsf@gmail.com> <87bjy03fql.fsf@gmail.com> <356d63bc-818c-428c-b31b-a0eb227b3a8a@gmx.at> <87o720gjst.fsf@gmail.com> <7dfe87a0-b367-47df-86df-f8fd95163fd6@gmx.at> <8734jbz8sb.fsf@gmail.com> <50ab6c0a-6afb-4727-9094-178668fc4f4e@gmx.at> <87zflfc3cd.fsf@gmail.com> <87y10ycxv6.fsf@gmail.com> <87mshbiiee.fsf@bauherren.ovh> <30c31060-6be4-4dab-8e49-c6f543821ea5@gmx.at> <87y10ecxk9.fsf@bauherren.ovh> <787e1cf0-810c-4eea-8d89-03ee83f67807@gmx.at> <87ttb2cof2.fsf@bauherren.ovh> <87seqmf9uj.fsf@mail.linkov.net> <877c7x4v4k.fsf@bauherren.ovh> <87msgtsc0t.fsf@mail.linkov.net> <87frmlnvyw.fsf@bauherren.ovh> <87seqktcvj.fsf@mail.linkov.net> Reply-To: Pranshu Sharma Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="37932"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: Pranshu Sharma via "Emacs development discussions." , martin rudalics , "eliz@gnu.org" To: Juri Linkov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Dec 20 13:11:46 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tObr6-0009az-Oy for ged-emacs-devel@m.gmane-mx.org; Fri, 20 Dec 2024 13:11:44 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tObq1-0007l5-IC; Fri, 20 Dec 2024 07:10:37 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tOZzK-0002EI-SE for emacs-devel@gnu.org; Fri, 20 Dec 2024 05:12:07 -0500 Original-Received: from mail.bauherren.ovh ([45.32.179.127]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tOZzI-0003gp-Nv; Fri, 20 Dec 2024 05:12:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=bauherren.ovh; s=mail; t=1734689518; bh=KVKzh3rwqO9R8woJ+rrScCq6YhwFJ5xftRwPkQnb6m4=; h=From:To:Cc:Subject:In-Reply-To:References:Date:From; b=lBuCHieivmiddWYQyvPYZgxnM8WJebbJXViDEAOHMabSw6qA69yZabQrcMm0X5k9a qLBxKtP8mPxFnNWEmhRoSCPfN8YZ7osYK9qCERTurFChYHrrgr+LQ+CmK1s6n06JCS jQiIymJZpfbU4mZPyKt4PrBAm8lsCeZqTCqbZMVK+ycZhlWcoq8WmxsA98LyK0s+vh YBKERrdd2cjrMfZTFh6KgrclwxHXbhklTrIEW8BqIsI+qoCFi8HOmANJ5szIU/YIEd iYhohtj5CDmVlev4yt6ov6JOcb9RSoTLvOzHvH+Ufa2/4d7rNQbqf9E8RJ807iigp9 Xchctgl8GYCow== In-Reply-To: <87seqktcvj.fsf@mail.linkov.net> (Juri Linkov's message of "Thu, 19 Dec 2024 09:10:52 +0200") Received-SPF: pass client-ip=45.32.179.127; envelope-from=pranshu@bauherren.ovh; helo=mail.bauherren.ovh X-Spam_score_int: -8 X-Spam_score: -0.9 X-Spam_bar: / X-Spam_report: (-0.9 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, FROM_FMBLA_NEWDOM28=0.798, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 20 Dec 2024 07:10:28 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:326795 Archived-At: --=-=-= Content-Type: text/plain Juri Linkov writes: > Please send the final diff. Use the diff Martin sent, and for the transpose functions, the file is attached. Too much is unkown for me to send a proper diff making window-x.el and adding keybindings. >> ideas? > > I have no more ideas for better keybidnings. Ok, then we can leave that specific binding unbound for now. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=window-transpose.el Content-Transfer-Encoding: quoted-printable ;;; -*- lexical-binding:t -*- (defun window-tree-normal-sizes (window &optional next) "Return normal sizes of all windows rooted at WINDOW. A list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT PARENT-WIN-WIDTH W1 W2 ...) is returned. SPLIT-TYPE is non-nil if PARENT-WIN is split horizontally. PARENT-WIN is the internal window. PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of PARENT-WIN. Wn is a list of the form (WINDOW HEIGHT WIDTH) where HEIGHT and WIDTH are the normal height and width of the window." (let (list) (while window (setq list (cons (cond ((window-top-child window) (append (list t window (window-normal-size window nil) (window-normal-size window t)) (window-tree-normal-sizes (window-top-child window) t))) ((window-left-child window) (append (list nil window (window-normal-size window nil) (window-normal-size window t)) (window-tree-normal-sizes (window-left-child window) t))) (t (list window (window-normal-size window nil) (window-normal-size window t)))) list)) (setq window (when next (window-next-sibling window)))) (nreverse list))) (defun window--window-to-transpose (frame-or-window) "Return the window to be acted upon by `window--transpose'. If FRAME-OR-WINDOW is a window return FRAME-OR-WINDOW. If FRAME-OR-WINDOW is a frame, return FRAME-OR-WINDOW's main window. If FRAME-OR-WINDOW is nil, than the frames main window wil be returned. If FRAME-OR-WINDOW is non-nil, and not a frame or a window or a number, than the return value will be the parent window of the selected window." (cond ((windowp frame-or-window) frame-or-window) ((or (framep frame-or-window) (not frame-or-window)) (window-main-window frame-or-window)) (frame-or-window (window-parent)))) (defun rotate-window-layout-anticlockwise (&optional frame-or-window) "Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees. Transform the layout of windows such that a window on top becomes a window on the right, a window on the right moves to the bottom, a window on the bottom moves to the left and a window on the left becomes one on the top. If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(right . above) nil))) (defun rotate-window-layout-clockwise (&optional frame-or-window) "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees. Transform the layout of windows such that a window on top becomes a window on the right, a window on the right moves to the bottom, a window on the bottom moves to the left and a window on the left becomes one on the top. If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(left . below) nil))) (defun flip-window-layout-horizontally (&optional frame-or-window) "Horizontally flip windows of FRAME-OR-WINDOW. Flip the window layout so that the window on the right becomes the window on the left, and vice-versa. If FRAME-OR-WINDOW is nil, flip the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(below . left) t))) (defun flip-window-layout-vertically (&optional frame-or-window) "Verticlly flip windows of FRAME-OR-WINDOW. Flip the window layout so that the top window becomes the bottom window and vice-versa. If FRAME-OR-WINDOW is nil, flip the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(above . right) t))) (defun transpose-window-layout (&optional frame-or-window) "Transpose windows of FRAME-OR-WINDOW. Make the windows on FRAME-OR-WINDOW so that every horizontal split becomes a vertical split, and vice versa. If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (window--transpose window '(right . below) nil))) (defun window--depmap(fun ls) "Map FUN across all nodes of list LS." (if (consp ls) (cons (if (consp (car ls)) (window--depmap fun (car ls)) (funcall fun (car ls))) (window--depmap fun (cdr ls))) (funcall fun ls))) ;; TODO elaborate (defun rotate-window-layout-reverse(&optional frame-or-window) "Move windows into locations of their predecessors in cyclic ordering. If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (rotate-window-layout frame-or-window t)) (defun rotate-window-layout (&optional frame-or-window reverse) "Move windows into locations of their forerunners in cyclic ordering. If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main window of that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that window. In any other case and interactively with a prefix argument rotate the parent window of the selected window." (interactive "P") (let ((window (window--window-to-transpose frame-or-window))) (if (or (not window) (window-live-p window)) (message "No windows to transpose") (let* ((frame (window-frame window)) (selected-window (frame-selected-window window)) (win-tree (car (window-tree-normal-sizes window))) (winls (seq-filter 'window-live-p (flatten-list win-tree))) (rotated-ls (if reverse (append (cdr winls) (list (car winls))) (append (last winls) winls))) (other-window-arg (if reverse 1 -1)) (first-window (car rotated-ls)) (new-win-tree (window--depmap (lambda (x) (if (window-live-p x) (pop rotated-ls) x)) win-tree))) (if (or (seq-some 'window-atom-root winls) (seq-some 'window-fixed-size-p winls)) (message "This does not work with fixed size or atom windows.") (progn ;; All child windows need to be recursively deleted. (delete-other-windows-internal first-window window) ;; (delete-dups atom-windows) (window--transpose-1 new-win-tree first-window '(below . right) t ni= l) (set-frame-selected-window frame selected-window) (other-window other-window-arg) (while (not (memq (selected-window) winls)) (other-window other-window-arg)))))))) (defun window--transpose (window conf no-resize) "Rearrange windows of WINDOW recursively. CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where HORIZONTAL-SPLIT will be used as the third argument of `split-window' when splitting a window that was previously horizontally split, and VERTICAL-SPLIT as third argument of `split-window' for a window that was previously vertically split. If NO-RESIZE is nil, the SIDE argument of the window-split is converted from vertical to horizontal or vice versa, with the same proportion of the total split." (if (or (not window) (window-live-p window)) (message "No windows to transpose") (let* ((frame (window-frame window)) (first-window window) (selected-window (frame-selected-window window)) (win-tree (car (window-tree-normal-sizes window))) (win-list (seq-filter 'window-live-p (flatten-list win-tree))) (atom-windows (remq nil (mapcar 'window-atom-root win-list)))) (if (and (not (eq (car atom-windows) window)) (or no-resize (and (not atom-windows) (not (seq-some 'window-fixed-size-p win-list))))) (progn (delete-dups atom-windows) (while (not (window-live-p first-window)) (setq first-window (window-child first-window))) (delete-other-windows-internal first-window window) (window--transpose-1 win-tree first-window conf no-resize atom-windows) ;; Go back to previously selected window. (set-frame-selected-window frame selected-window) (mapc 'window-make-atom atom-windows)) (message "This does not work with fixed size or atom windows."))))) (defun window--transpose-1 (subtree cwin conf no-resize atom-windows) "Subroutine of `window--transpose'. SUBTREE must be in the format of the result of `window-tree-normal-sizes'. CWIN is the current window through which the window splits are made. ATOM-WINDOWS is a list of internal atom windows. The CONF and NO-RESIZE arguments are the same as the ones in `window--transpose'." ;; `flen' is max size the window could be converted to the opposite ;; of the given split type. (let ((parent-window-is-set t) (flen (if (funcall (if no-resize 'not 'identity) (car subtree)) (float (window-pixel-width cwin)) (float (window-pixel-height cwin))))) (mapc (pcase-lambda (`(,window . ,size)) (prog1 (let* ((split-size (- (round (* flen size)))) (split-type (funcall (if (car subtree) 'car 'cdr) conf)) (return-win (if (listp window) ;; `window' is a window subtree. ;; `first-child' is a live window that is an descended of window (let* ((first-child window) ;; If the window being split is atomic (is-atom ;; cadr will return the internal parent window (memq (cadr first-child) atom-windows))) ;; (caar (cddddr first-child)) is the first window in the ;; list if there is a live window. (while (not (windowp (caar (cddddr first-child)))) (setq first-child (car (cddddr first-child)))) (window--transpose-1 window (let ((window-combination-limit parent-window-is-set)) (split-window cwin split-size split-type t (if window-combination-limit (cons (caar (cddddr first-child)) (cadr subtree)) (caar (cddddr first-child))))) (if is-atom '(nil . t) conf) no-resize atom-windows)) ;; `window' is a window. (split-window cwin split-size split-type t ;; We need to set parent window if it hasn't been set ;; already. (if parent-window-is-set (cons window (cadr subtree)) window))))) (when (eq window-combination-limit t) (set-window-combination-limit (cadr subtree) nil)) return-win) (setq parent-window-is-set nil))) (mapcar (lambda (e) (pcase-let* ((`(,window . ,window-size-info) (if (windowp (car e)) (cons (car e) e) (cons e (cdr e))))) (cons window ;; The respective size of the window. (if (car subtree) (cadr window-size-info) (caddr window-size-info))))) ;; We need to ingore first 5 elements of window list, we ignore ;; window split type, sizes and the first window (it's ;; implicitly created). We just have a list of windows. (nreverse (cdr (cddddr subtree))))) ;; (caar (cddddr subtree)) is the first child window of subtree. (unless (windowp (caar (cddddr subtree))) (let ((is-atom (memq (cadr (cadr (cddddr subtree))) atom-windows))) (window--transpose-1 (car (cddddr subtree)) cwin (if is-atom '(nil . t) co= nf) no-resize atom-windows))))) (define-key global-map (kbd "C-x w t") 'transpose-window-layout) (define-key global-map (kbd "C-x w c") 'rotate-window-layout) (define-key global-map (kbd "C-x w r") 'rotate-window-layout-clockwise) (define-key global-map (kbd "C-x w f") 'flip-window-layout-vertically) (define-key global-map (kbd "C-x w h") 'flip-window-layout-horizontally) --=-=-= Content-Type: text/plain -- Pranshu Sharma --=-=-=--