[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: `\textLengthOn` for `TextSpanner` grobs

From: Jean Abou Samra
Subject: Re: `\textLengthOn` for `TextSpanner` grobs
Date: Tue, 30 Nov 2021 21:55:59 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1

Le 28/11/2021 à 16:10, Werner LEMBERG a écrit :
please have a look at this example

    \set Score.skipBars = ##t
    \override TextSpanner.bound-details.left.text = "poco a poco tempo I"
    \override TextSpanner.bound-details.right.text = "Tempo I"
    R1*4\startTextSpan |

What is the equivalent of `\textLengthOn` for `TextSpanner` grobs so
that the multi-measure rest gets properly stretched horizontally?

   \override TextSpanner.minimum-length = 30
   \override TextSpanner.springs-and-rods = #ly:spanner::set-spacing-rods
Thanks.  Alas, doesn't work correctly for broken spanners:

\paper {
   line-width = 100\mm

   \set Score.skipBars = ##t
   \override TextSpanner.bound-details.left.text = "poco a poco tempo I"
   \override TextSpanner.bound-details.left-broken.text = ""
   \override TextSpanner.bound-details.right.text = "Tempo I"
   \override TextSpanner.bound-details.right-broken.text = ""
   \override TextSpanner.minimum-length = 34
   \override TextSpanner.springs-and-rods = #ly:spanner::set-spacing-rods

   R1*4\startTextSpan | \break
   R1*4\stopTextSpan |
   R1 |

I also wonder whether there is a solution (or should be a solution)
that doesn't need manual entering of a minimum length value that can
only be found by trial and error.  For example, wouldn't it be
sensible to have the possibility of

\override TextSpanner.bound-details
             .left.extra-spacing-height = #'(-inf.0 . +inf.0)
\override TextSpanner.bound-details
             .right.extra-spacing-height = #'(-inf.0 . +inf.0)

in combination with the `extra-spacing-width` property to get
automatically the correct value(s)?


Here is what I could arrive at this evening:

\version "2.23.6"

\paper {
  line-width = 100\mm

#(use-modules (ice-9 match)
              (srfi srfi-26))

#(define (define-grob! grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                  ((System) '((system-interface
                                  (else '(unknown-interface)))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)

#(define (text-spanner-bound-stub::text grob)
   (let ((details (ly:grob-property grob 'bound-details)))
     (chain-assoc-get 'text details empty-markup)))

#(define (text-spanner-bound-stub::X-offset grob)
   (let* ((details (ly:grob-property grob 'bound-details))
          (padding (chain-assoc-get details 'padding 0))
          (column (ly:grob-parent grob X))
          (direction (ly:grob-property grob 'direction))
          (text-spanner (ly:grob-parent grob Y))
          (parent-bound (ly:spanner-bound text-spanner direction))
          (extent (ly:generic-bound-extent parent-bound column))
          (attach (chain-assoc-get details 'attach-dir CENTER)))
     (- (interval-index extent attach)
        (* padding direction))))

#(define-grob! 'TextSpannerBoundStub
   `((Y-extent . ,grob::always-Y-extent-from-stencil)
     (after-line-breaking . ,ly:grob-suicide!)
     (text . ,text-spanner-bound-stub::text)
     (stencil . ,ly:text-interface::print)
     (font-shape . italic)
     (X-offset . ,text-spanner-bound-stub::X-offset)
     ;; Watch the hack!
     ;; NB: This relies on an arcane inconsistency that should
     ;; really be fixed...
     (cross-staff . #t)
     (meta . ((classes . (Item))
              (interfaces . ())))))

#(define (transform-nested-path! path transform alist)
   (match path
     (() (transform alist))
     ((key . rest)
      (assoc-set! alist key (transform-nested-path! rest transform (assoc-get key alist))))))

  '(TextSpanner meta interfaces)
  (cute cons 'text-spanner-interface <>)

#(define (Text_spanner_stub_engraver context)
   (let ((text-spanner #f))
     (define (get-details syms)
       (let ((bound-details (ly:grob-property text-spanner 'bound-details)))
         (map (cute assoc-get <> bound-details '())
     (define (new-stub engraver)
       (let ((stub (ly:engraver-make-grob engraver 'TextSpannerBoundStub '())))
         (ly:grob-set-parent! stub Y text-spanner)
         ((text-spanner-interface engraver grob source-engraver)
            (set! text-spanner grob)
            (let ((stub (new-stub engraver)))
              (ly:grob-set-parent! stub Y grob)
              (ly:grob-set-property! stub 'direction LEFT)
              (ly:grob-set-property! stub 'bound-details (get-details '(left))))))
       ((process-music engraver)
          (if text-spanner
              (let ((column (ly:context-property context 'currentCommandColumn))
                    (stub-left (new-stub engraver))
                    (stub-right (new-stub engraver)))
                (ly:grob-set-property! stub-left 'bound-details (get-details '(left-broken left)))
                (ly:grob-set-property! stub-left 'direction LEFT)
                (ly:grob-set-property! stub-left 'break-visibility #(#f #f #t))
                (ly:grob-set-property! stub-left 'non-musical #t)
                (ly:grob-set-property! stub-right 'bound-details (get-details '(right-broken right)))
                (ly:grob-set-property! stub-right 'direction RIGHT)
                (ly:grob-set-property! stub-right 'break-visibility #(#t #f #f))
                (ly:grob-set-property! stub-right 'non-musical #t))))
          ((text-spanner-interface engraver grob source-engraver)
             (let ((stub (new-stub engraver)))
               (ly:grob-set-property! stub 'bound-details (get-details '(right)))
               (ly:grob-set-property! stub 'direction RIGHT))
             (set! text-spanner #f))))))

\layout {
  \context {
    \grobdescriptions #all-grob-descriptions
  \context {
    \consists #Text_spanner_stub_engraver

  \set Score.skipBars = ##t
  \override TextSpanner.bound-details.left.padding = -10
  \override TextSpanner.color = red
  \override TextSpanner.bound-details.left.text = "poco a poco tempo I"
  \override TextSpanner.bound-details.left-broken.text = ""
  \override TextSpanner.bound-details.right.text = "Tempo I"
  \override TextSpanner.bound-details.right-broken.text = ""

  R1*4\startTextSpan |
  R1*4\stopTextSpan |
  R1 |

- The spacing is not pretty;
- The code is not pretty.

It is a proof of concept. Even though I'd probably go
for a somewhat different if similar approach if I
were actually trying to integrate this into LilyPond.

Basically, extra-spacing-width etc. work on items, and
not on spanners. If you want a spanner to occupy space
at its bounds, the state of the art is that you have
to create "stub" items.

I won't have the time to work on it more, sorry.

Does David N's text-spanner work solve this problem, I wonder?

Nice, I didn't know about it. It doesn't handle this as
far as I can see, however.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]