Subject: bug#62606: [PATCH] function to align mode-line elements to right

From: [email-masked]
Date: Sat, 06 May 2023 15:14:24 +0100

Thanks for the feedback. A new patch is attached.

> From: 	Eli Zaretskii
> Date: 	Sun, 02 Apr 2023 08:47:40 +0300

> What would be the situations where the user could want this alignment?

It's merely an aesthetic thing -- I just like having some of my
information right-aligned. I think the motivation for the tab bar
right-align function is similar?

> One possible issue with right alignment is that when the total size of
> the mode-line text changes (e.g., the mode lighter changes), the
> leftmost part of it will move horizontally, something that could be
> unpleasant and even annoying.

This doesn't happen with my proposed setup (though I agree it would be
annoying if it did!). The user includes the symbol
`mode-line-format-right-align' in their mode line format. This is a
variable which holds a mode-line construct `(:eval
(mode-line-format-right-align))'. So whenever the mode-line is reloaded
(and therefore, whenever the length could change), the space used to pad
the right-aligned part to the right is recalculated. The only situation
where this could go wrong would be if the length of the left part of the
mode-line changed /during/ the process of reloading the mode-line, which
seems unlikely to happen.

> Also, what happens if the mode-line text is longer than the window
> width.  What are user expectations in that case, and does :align-to
> meet those expectations?

Then the text overflows to the right. The behaviour with my patch is the
same as without: the text furthest to the right is not visible. The way
the patch is written would mean that the padding used to align text to
the right would be nil (i.e. a zero-width) string, but this is not
really relevant because it won't be visible (because it will be off the
right-hand edge of the screen).

> > The functin aligns to the right /fringe/ rather than the margin because
> > some modes (e.g. [visual-fill-column-mode]) mess around the margin size,
> > but the right-align should be unaffected by this.
> This detail should not be hard-coded, IMO.

On reflection, I think you're right.

> It should be up to the calling Lisp program (or maybe even user
> option) whether the alignment is to the right margin or to the window
> edge.

I found this easier to implement as a user option -- I've added
`mode-line-right-align-edge' to the patch.

> Also, did you test this when the fringe is inside the margins and the
> margins have non-zero width?

Yes. It does exactly what you expect (i.e. if
`mode-line-right-align-edge' is set to `right-fringe' then align
to the fringe, it set to `right-margin' etc.). Setting
`mode-line-right-align-edge' to `window' aligns to the right of
the window even in these circumstances.

> The call to string-pixel-width will significantly slow down redisplay
> of the mode line.

Yes, unfortunately.

> I wonder whether this feature should be implemented in C instead, to
> avoid the slowdown.

This would be great, but I definitely don't have the skills to do it.
Unless someone else does this, perhaps we could include my lisp
implementation for now, document the slowness (in the docstring),
and put a C implementation on the wishlist/TODO?

> This should use display-graphic-p, not window-system.

Fixed.

> Did you test this in a session which has both GUI and TTY frames?

Yes, it behaves correctly (i.e. right-aligns in both, even when the
same buffer is simultaneously displayed in both).

> From: 	Juri Linkov
> Date: 	Sun, 02 Apr 2023 19:19:01 +0300

> Here `format-mode-line' returns an empty string when
> `mode-line-format-right-align' was added in the middle of
> `mode-line-format'. For a strange reason, it expects that
> `mode-line-format' should start with a string. So everything is fine
> after replacing in your patch

Thanks for pointing this out. I've fixed it in the new patch (though my
fix uses a back-quote construction, but the effect is the same).

Hope the new version is better. Thoughts?

From 770e2a0617c650755f1be3de15ffbe13a9664a53 Mon Sep 17 00:00:00 2001
From: Hugo Heagren <[email-masked]>
Date: Sat, 1 Apr 2023 22:27:25 +0100
Subject: [PATCH] Support right-align in mode-line

* lisp/bindings.el (mode-line-right-align-edge):  New custom variable.
Controls where `mode-line-format-right-align' should align to.
(mode-line-format-right-align):  New function.  If the symbol
`mode-line-format-right-align' appears in `mode-line-format', then
return return a padding string which aligns everything after that
symbol to the right.  Padding width is altered with the display
property and depends on the value of `mode-line-right-align-edge'.
(mode-line-format-right-align):  New variable.  Convenience definition
for including right alignment in `mode-line-format'.
---
 lisp/bindings.el | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 34aa8399a96..7ba9f325061 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -304,6 +304,70 @@ mode-line-process
 ;;;###autoload
 (put 'mode-line-process 'risky-local-variable t)
 
+(defcustom mode-line-right-align-edge 'window
+  "Where function `mode-line-format-right-align' should align to.
+
+Must be set to a symbol.  Acceptable values are:
+- window: align to extreme right of window, regardless of margins
+  or fringes
+- right-fringe: align to right-fringe
+- right-margin: align to right-margin
+- right: synonym for right-margin (supported because this is how the
+  display property understands this, see info node `(elisp)Specified
+  Space'.)"
+  :type '(choice (const right)
+                 (const right-margin)
+                 (const right-fringe)
+                 (const window))
+  :group 'mode-line)
+
+(defun mode-line-format-right-align ()
+  "Right-align all following mode-line constructs.
+
+When the symbol `mode-line-format-right-align' appears in
+`mode-line-format', return a string of one space, with a display
+property to make it appear long enough to align anything after
+that symbol to the right of the rendered mode line.  Exactly how
+far to the right is controlled by `mode-line-right-align-edge'.
+
+It is important that the symbol `mode-line-format-right-align' be
+included in `mode-line-format' (and not another similar construct
+such as `(:eval (mode-line-format-right-align)').  This is because
+the symbol `mode-line-format-right-align' is processed by
+`format-mode-line' as a variable."
+  (let* ((rest (cdr (memq 'mode-line-format-right-align
+			  mode-line-format)))
+	 (rest-str (format-mode-line `("" ,@rest)))
+	 (rest-width (string-pixel-width rest-str)))
+    (propertize " " 'display
+		;; The `right' spec doesn't work on TTY frames
+		;; when windows are split horizontally (bug#59620)
+		(if (and (display-graphic-p)
+                         (not (eq mode-line-right-align-edge 'window)))
+		    `(space :align-to (- ,mode-line-right-align-edge
+                                         (,rest-width)))
+		  `(space :align-to (,(- (window-pixel-width)
+                                         (window-scroll-bar-width)
+                                         (window-right-divider-width)
+                                         (* (or (cdr (window-margins)) 1)
+                                            (frame-char-width))
+                                         ;; Manually account for value of
+                                         ;; `mode-line-right-align-edge' even
+                                         ;; when display is non-graphical
+                                         (pcase mode-line-right-align-edge
+                                           ((or 'right 'right-margin)
+                                            (or (cdr (window-margins)) 0))
+                                           ('right-fringe
+                                            ;; what here?
+                                            (or (cadr (window-fringes)) 0))
+                                           (_ 0))
+                                         rest-width)))))))
+
+(defvar mode-line-format-right-align '(:eval (mode-line-format-right-align))
+  "Mode line construct to right align all following constructs.")
+;;;###autoload
+(put 'mode-line-format-right-align 'risky-local-variable t)
+
 (defun bindings--define-key (map key item)
   "Define KEY in keymap MAP according to ITEM from a menu.
 This is like `define-key', but it takes the definition from the
-- 
2.20.1