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

From: [email-masked]
Date: Mon, 19 Jun 2023 20:06:58 +0100

On 2023-06-09 17:03, Stefan Monnier wrote:
> Also, the code is fairly simple and self-contained, so I don't see
> a good reason not to include that.

Thanks :)

> I think symbols like `right-fringe` should be enclosed in `...'
> in docstrings.

Fixed in new patch, attached.

> Also, I see no reason to encourage the use of an alias,
> so I'd drop either `right-margin` or `right` from the doc (especially
> since it doesn't really come for free in the code).

Fixed. Dropped `right', as it was the less descriptive version.

> As a user I'd wonder if "align to right-fringe" means to align to
> the beginning (i.e. left side) or end (i.e. right side) of the
> right fringe.

Documented (and included a link to relevant info node).

> AFAICT, this function is internal to the implementation of the
> `mode-line-format-right-align` mode-line spec.  So maybe it should use
> "--" in its name.

You're right -- Done.

> This is the ugly part of the implementation

Yes, I agree :(

> an alternative is to use
> 
>     (..LEFT.. (:eval (mode-line-format-right-align ..RIGHT..)))

I share your technical worries about this. I worry too that this might 
end
up being more confusing for users, because it invites such constructions
as:

(..LEFT.. (:eval (mode-line-format-right-align ..RIGHT..)) ..WHAT..)

Where the behaviour of ..WHAT.. is at best to be completely hidden, and
at worst undefined. If we use a single symbol, this can't happen.

I also think that, as a user, it's good to have consistent interfaces
for this sort of thing, and my proposed function is intended to work
just like `tab-bar-format-align-right', which is included as a symbol,
not a more complex construction.

> should also be usable in `header-line-format` and friends.

This would good though.

Hugo

From b9fb6399f38f250e9629e8350108ea934d4cfd97 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 | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 34aa8399a96..d6f12e64399 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -304,6 +304,69 @@ 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.
+Internally, that function uses `:align-to' in display property,
+so aligns to the left edge of the given area.  See info node
+`(elisp)Pixel Specification'.
+
+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"
+  :type '(choice (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
+                                           ('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