From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: martin rudalics Newsgroups: gmane.emacs.bugs Subject: bug#47300: delete-window to select window with same position Date: Wed, 2 Jun 2021 11:08:48 +0200 Message-ID: <6f37ba16-527b-4537-116c-b215a9f39d1b@gmx.at> References: <87a6qw43gg.fsf@mail.linkov.net> <87pmxodrmq.fsf@gnus.org> <7f870f9b-95ad-6b5d-82aa-1bcfe5cc880a@gmx.at> <87fsyk0w92.fsf@mail.linkov.net> <2ec3b911-4fb1-4b13-a5b8-28278a5c43ba@gmx.at> <87h7iyzvh5.fsf@mail.linkov.net> <0c239adb-e9d5-1fe9-4c4c-2da1603002f7@gmx.at> <87mtsmmpey.fsf@mail.linkov.net> <1032ab23-3905-1b3f-917c-60ccd12337b3@gmx.at> <87v9752n4r.fsf@mail.linkov.net> <63612145-5eaa-0766-30cc-f30f7e7e1700@gmx.at> <87k0nefwq6.fsf@mail.linkov.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------15EAAD56E2E096153F467FFA" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="1762"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Lars Ingebrigtsen , 47300@debbugs.gnu.org To: Juri Linkov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Wed Jun 02 11:09:10 2021 Return-path: Envelope-to: geb-bug-gnu-emacs@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 1loMs9-0000G3-RI for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 02 Jun 2021 11:09:09 +0200 Original-Received: from localhost ([::1]:50368 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1loMs8-0000sx-QG for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 02 Jun 2021 05:09:08 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:53610) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1loMs2-0000sR-TQ for bug-gnu-emacs@gnu.org; Wed, 02 Jun 2021 05:09:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:55536) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1loMs2-0004sg-MM for bug-gnu-emacs@gnu.org; Wed, 02 Jun 2021 05:09:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1loMs2-0004KB-HX for bug-gnu-emacs@gnu.org; Wed, 02 Jun 2021 05:09:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: martin rudalics Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Wed, 02 Jun 2021 09:09:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 47300 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 47300-submit@debbugs.gnu.org id=B47300.162262493916613 (code B ref 47300); Wed, 02 Jun 2021 09:09:02 +0000 Original-Received: (at 47300) by debbugs.gnu.org; 2 Jun 2021 09:08:59 +0000 Original-Received: from localhost ([127.0.0.1]:38849 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1loMrz-0004Jt-47 for submit@debbugs.gnu.org; Wed, 02 Jun 2021 05:08:59 -0400 Original-Received: from mout.gmx.net ([212.227.15.19]:59469) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1loMrw-0004Jf-KZ for 47300@debbugs.gnu.org; Wed, 02 Jun 2021 05:08:57 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1622624930; bh=Nivl9xTGWv26N86Ct4POKjlS1jyKh1LtktOuOBXi/bw=; h=X-UI-Sender-Class:Subject:To:Cc:References:From:Date:In-Reply-To; b=WyfCD5dRbNCFVsLIKZu4VRv/mg1nQMBdHjMF4asuuBdRSI/m1setDspuRS8q15FA5 XqbeZKCFpU/OnI4mHfPt1f7AQq6jUNTKgwl3DB9o2RlUw6D3cTxK1hGrB9rzCSPIKr pidAimO1A+WxSJaiLV8bH/iCqSE12rLvNBlt4k8k= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Original-Received: from [192.168.1.100] ([46.125.249.108]) by mail.gmx.net (mrgmx004 [212.227.17.190]) with ESMTPSA (Nemesis) id 1MRCK6-1m0Q6v3Ewe-00N7rs; Wed, 02 Jun 2021 11:08:49 +0200 In-Reply-To: <87k0nefwq6.fsf@mail.linkov.net> Content-Language: en-US X-Provags-ID: V03:K1:BhRNYk7qHy46mqzZjGT49rmB+1zwR+Myyi/zcR0qVFmNEeoZHmN jXggjonZBKjC4XbACJxzFWWrgepCNrD8ZzRslWnAuh7K7xpGLe5fEKUwIY5NP5GUdgWgS54 uTCgmNXMQnnOkxEQ/MmWILRcVDTf/bVvbCQa58jguu7c88VjAWbfkO1W87MTRaaVrALovUP /dkk1kK7BY6ABNeY07zng== X-UI-Out-Filterresults: notjunk:1;V03:K0:Q84vFIFeNgU=:/YAPUN8E0HoqhGED1CbYQx 8nY6n6Nezl48Df8HC7gfxW72JsHQ58zsTgCry+s1KOERzoh8mcMAlsdVALrHnm0UbDKxMIoCZ X0vo1TkDeRTAMxO0cTwLcBg1y7LcurTkOaKW8ldy5d5rIHdkmoZWgou629GfpLzIUrsZk6eRm NA57X3KOnEYTSFgvvsK7TAs6tiey5bxoTy53F21rchnp+omHVVBtvKCgvmHN64jFBA4oKqJTY Rq4KdYdL/PvKovh2kuXH5c1Jaeb0NEd/T+MszAVaCDAiP89HwlZDRNnFxosk/y4HqrjG7MSK1 eo+pNkiy9QDUUeqhbhk38X2iDn3/8hbBeI85sMB8QPMiGllsMFSFk3CchRlV5cA6H8odBHYVr EWidJFihOp0fviIwsbfEgqFx8X8VAx/FUogU6K85TSJugWXXB7tfQwVDfjSkh3M6/zcGFSxd0 Qw97AAjcBzwKmehWNgwM6TN5pQrzko4lm4UMBHVmNKSjHfm4NXUrSWZWd61v9FcZGxPjlAYqm 0Z1/7jQlS92VlFd2GjeEaazLZi34ZzfDscbN/fBu/QLHurJ02v5XB6hzuq1dKlymdUZgFfq0u i3KsEA8xyCMgCXKGlnutrU+tw7gjCWDn8Hi39Dv9LPH2hMIezRCb9OjHT1+SUzeceP2PBI+7U ccJW6gniHPSAY+1j/Gy7/M9e4Vgo43uX67frFhY08FU/6mOzdM+v/s3ojo3KfPMVzbQnkXrla 2Va3TFmAohwKFxWfzgT936iHUtUmqnsCRoYaKbdHhQeNWB1f87YHyhK7OTSfyykedArZxyHR X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:207854 Archived-At: This is a multi-part message in MIME format. --------------15EAAD56E2E096153F467FFA Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit > I changed <= to < to fix only Y coordinates for top/bottom windows, > but maybe X coordinates should still use <= ? The function has to treat the case where an edge is shared between two windows separately. A non-shared edge always belongs to the corresponding window. > Please note that the crucial difference is that now it uses > frame relative positions rather than window relative ones > with '(window-edges window nil nil t)' of the window to be deleted, > and window coordinates added to X and Y of posn-at-point. Right. But the X and Y values are needed separately only at the time `window-at-pos' is called. > 1. `get-mru-window' could be one possible choice of the new option. > 2. `use-posn-at-point' could be another choice. I called them just 'mru' and 'pos' now. The option is called `selected-window-after-deletion', there might be better names. > 3. Selecting the first window could remain in `delete-window-internal'. It has to stay there. > 4. But what to do with this code block with '(other-window -1 frame)'? > Should this be simply deleted? That `other-window' was a bug: A frame that is not selected should not get selected because we delete a window on it. In addition, the old code did not record a newly selected window unless that window was produced by `get-mru-window'. I tried to fix those too. Please have a look. martin --------------15EAAD56E2E096153F467FFA Content-Type: text/x-patch; name="selected-window-after-deletion.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="selected-window-after-deletion.diff" diff --git a/lisp/window.el b/lisp/window.el index fd1c617d6b..20f664c6f5 100644 =2D-- a/lisp/window.el +++ b/lisp/window.el @@ -2538,12 +2538,14 @@ get-lru-window (setq best-window window))))) (or best-window second-best-window))) -(defun get-mru-window (&optional all-frames dedicated not-selected) +(defun get-mru-window (&optional all-frames dedicated not-selected no-oth= er) "Return the most recently used window on frames specified by ALL-FRAME= S. A minibuffer window is never a candidate. A dedicated window is never a candidate unless DEDICATED is non-nil, so if all windows are dedicated, the value is nil. Optional argument NOT-SELECTED -non-nil means never return the selected window. +non-nil means never return the selected window. Optional +argument NO_OTHER non-nil means to never return a window whose +'no-other-window' parameter is non-nil. The following non-nil values of the optional argument ALL-FRAMES have special meanings: @@ -2565,7 +2567,9 @@ get-mru-window (setq time (window-use-time window)) (when (and (or dedicated (not (window-dedicated-p window))) (or (not not-selected) (not (eq window (selected-window)))) - (or (not best-time) (> time best-time))) + (or (not no-other) + (not (window-parameter window 'no-other-window))) + (or (not best-time) (> time best-time))) (setq best-time time) (setq best-window window))) best-window)) @@ -4130,18 +4134,53 @@ window-deletable-p ;; of its frame. t)))) -(defun window--in-subtree-p (window root) - "Return t if WINDOW is either ROOT or a member of ROOT's subtree." - (or (eq window root) - (let ((parent (window-parent window))) - (catch 'done - (while parent - (if (eq parent root) - (throw 'done t) - (setq parent (window-parent parent)))))))) +(defun window-at-pos (x y &optional frame no-other) + "Return live window at coordinates X, Y on specified FRAME. +X and Y are counted in pixels from an origin at 0, 0 of FRAME's +native frame. A coordinate on an edge shared by two windows is +attributed to the window on the right (or below). Return nil if +no such window can be found. + +Optional argument FRAME must specify a live frame and defaults to +the selected one. Optional argument NO-OTHER non-nil means to +not return a window with a non-nil 'no-other-window' parameter." + (setq frame (window-normalize-frame frame)) + (let* ((root-edges (window-edges (frame-root-window frame) nil nil t)) + (root-left (nth 2 root-edges)) + (root-bottom (nth 3 root-edges))) + (catch 'window + (walk-window-tree + (lambda (window) + (let ((edges (window-edges window nil nil t))) + (when (and (>=3D x (nth 0 edges)) + (or (< x (nth 2 edges)) (=3D x root-left)) + (>=3D y (nth 1 edges)) + (or (< y (nth 3 edges)) (=3D y root-bottom))) + (if (and no-other (window-parameter window 'no-other-window)= ) + (throw 'window nil) + (throw 'window window))))) + frame)))) + +(defcustom selected-window-after-deletion 'mru + "How to choose a frame's selected window after window deletion. +When a frame's selected window gets deleted, Emacs has to choose +another live window on that frame to serve as its selected +window. This option allows to control which window gets selected +instead. + +The possible choices are 'mru' (the default) to select the most +recently used window on that frame and 'pos' to choose the window +at the position of point of the previously selected window. If +this is nil, choose the frame's first window instead. A window +with a non-nil 'no-other-window' parameter is never chosen." + :type '(choice (const :tag "Most recently used" mru) + (const :tag "At position" pos) + nil) + :group 'windows + :version "28.1") (defun delete-window (&optional window) - "Delete WINDOW. + "Delete specified WINDOW. WINDOW must be a valid window and defaults to the selected one. Return nil. @@ -4156,7 +4195,11 @@ delete-window `delete-window' with the root of the atomic window as its argument. Signal an error if WINDOW is either the only window on its frame, the last non-side window, or part of an atomic window -that is its frame's root window." +that is its frame's root window. + +If WINDOW is the selected window on its frame, choose some other +window as that frame's selected window according to the value of +the option `selected-window-after-deletion'." (interactive) (setq window (window-normalize-window window)) (let* ((frame (window-frame window)) @@ -4191,11 +4234,11 @@ delete-window (window-combination-resize (or window-combination-resize (window-parameter parent 'window-side))) - (frame-selected - (window--in-subtree-p (frame-selected-window frame) window)) + (frame-selected-window (frame-selected-window frame)) ;; Emacs 23 preferably gives WINDOW's space to its left ;; sibling. - (sibling (or (window-left window) (window-right window)))) + (sibling (or (window-left window) (window-right window))) + frame-selected-window-edges frame-selected-window-pos) (window--resize-reset frame horizontal) (cond ((and (not (eq window-combination-resize t)) @@ -4211,15 +4254,63 @@ delete-window (t ;; Can't do without resizing fixed-size windows. (window--resize-siblings window (- size) horizontal t))) + + (when (eq selected-window-after-deletion 'pos) + ;; Remember edges and position of point of the selected window + ;; of WINDOW'S frame. + (setq frame-selected-window-edges + (window-edges frame-selected-window nil nil t)) + (setq frame-selected-window-pos + (nth 2 (posn-at-point nil frame-selected-window)))) + ;; Actually delete WINDOW. (delete-window-internal window) (window--pixel-to-total frame horizontal) - (when (and frame-selected - (window-parameter - (frame-selected-window frame) 'no-other-window)) - ;; `delete-window-internal' has selected a window that should - ;; not be selected, fix this here. - (other-window -1 frame)) + + ;; If we deleted the selected window of WINDOW's frame, choose + ;; another one based on `selected-window-after-deletion'. Note + ;; that both `window-at-pos' and `get-mru-window' mail fail to + ;; produce a suitable window in which case we will fall back on + ;; its frame's first window, chosen by `delete-window-internal'. + (cond + ((window-live-p frame-selected-window)) + ((and frame-selected-window-pos + ;; We have a recorded position of point of the previously + ;; selected window. Try to find the window that is now + ;; at that position. + (let ((new-frame-selected-window + (window-at-pos + (+ (nth 0 frame-selected-window-edges) + (car frame-selected-window-pos)) + (+ (nth 1 frame-selected-window-edges) + (cdr frame-selected-window-pos)) + frame t))) + (and new-frame-selected-window + ;; Select window at WINDOW's position at point. + (set-frame-selected-window + frame new-frame-selected-window))))) + ((and (eq selected-window-after-deletion 'mru) + ;; Try to use the most recently used window. + (let ((mru-window (get-mru-window frame nil nil t))) + (and mru-window + (set-frame-selected-window frame mru-window))))) + ((and (window-parameter + (frame-selected-window frame) 'no-other-window) + ;; If `delete-window-internal' selected a window with a + ;; non-nil 'no-other-window' parameter as its frame's + ;; selected window, try to choose another one. + (catch 'found + (walk-window-tree + (lambda (other) + (unless (window-parameter other 'no-other-window) + (set-frame-selected-window frame other) + (throw 'found t))) + frame)))) + (t + ;; Record the window chosen by `delete-window-internal'. + (set-frame-selected-window + frame (frame-selected-window frame)))) + (window--check frame) ;; Always return nil. nil)))) diff --git a/src/window.c b/src/window.c index 2d98ae5f15..db324effcc 100644 =2D-- a/src/window.c +++ b/src/window.c @@ -5148,15 +5148,13 @@ DEFUN ("delete-window-internal", Fdelete_window_in= ternal, Sdelete_window_interna adjust_frame_glyphs (f); if (!WINDOW_LIVE_P (FRAME_SELECTED_WINDOW (f))) - /* We deleted the frame's selected window. */ + /* We apparently deleted the frame's selected window; use the + frame's first window as substitute but don't record it yet. + `delete-window' may have something better up its sleeves. */ { /* Use the frame's first window as fallback ... */ Lisp_Object new_selected_window =3D Fframe_first_window (frame); - /* ... but preferably use its most recently used window. */ - Lisp_Object mru_window; - /* `get-mru-window' might fail for some reason so play it safe - - promote the first window _without recording it_ first. */ if (EQ (FRAME_SELECTED_WINDOW (f), selected_window)) Fselect_window (new_selected_window, Qt); else @@ -5164,24 +5162,9 @@ DEFUN ("delete-window-internal", Fdelete_window_int= ernal, Sdelete_window_interna last selected window on F was an active minibuffer, we want to return to it on a later Fselect_frame. */ fset_selected_window (f, new_selected_window); - - unblock_input (); - - /* Now look whether `get-mru-window' gets us something. */ - mru_window =3D call1 (Qget_mru_window, frame); - if (WINDOW_LIVE_P (mru_window) - && EQ (XWINDOW (mru_window)->frame, frame)) - new_selected_window =3D mru_window; - - /* If all ended up well, we now promote the mru window. */ - if (EQ (FRAME_SELECTED_WINDOW (f), selected_window)) - Fselect_window (new_selected_window, Qnil); - else - fset_selected_window (f, new_selected_window); } - else - unblock_input (); + unblock_input (); FRAME_WINDOW_CHANGE (f) =3D true; } else --------------15EAAD56E2E096153F467FFA--