Ability to add labels to brackets is essential for musical analysis.
This patch introduces a new grob, "HorizontalBracketText," a spanner
created along with "HorizontalBracket" by Horizontal_bracket_engraver.
Repeated text of broken brackets is parenthesized.
@end ignore
+@item
+It is now possible to add text to analysis brackets through the
+@code{HorizontalBracketText} object.
+@lilypond[quote,verbatim]
+\layout {
+ \context {
+ \Voice
+ \consists "Horizontal_bracket_engraver"
+ }
+}
+
+{
+ \once \override HorizontalBracketText.text = "a"
+ c''\startGroup d''\stopGroup
+ e''-\tweak text "a'" \startGroup d''\stopGroup
+}
+@end lilypond
+
@item
The ends of hairpins may now be fine-tuned using the @code{shorten-pair}
grob property, which previously only affected text-spanners like
}
@end lilypond
-@seealso
-Snippets:
-@rlsr{Editorial annotations}.
+@snippets
+@lilypondfile[verbatim,quote,ragged-right,texidoc,doctitle]
+{analysis-brackets-above-the-staff.ly}
+
+@lilypondfile[verbatim,quote,ragged-right,texidoc,doctitle]
+{analysis-brackets-with-labels.ly}
+
+@seealso
Internals Reference:
@rinternals{Horizontal_bracket_engraver},
@rinternals{HorizontalBracket},
@rinternals{horizontal-bracket-interface},
+@rinternals{HorizontalBracketText},
+@rinternals{horizontal-bracket-text-interface},
@rinternals{Staff}.
--- /dev/null
+\version "2.19.55"
+
+\header {
+ lsrtags = "editorial-annotations, tweaks-and-overrides"
+
+ texidoc = "
+Text may be added to analysis brackets through the @code{text} property
+of the @code{HorizontalBracketText} grob. Adding different texts to
+brackets beginning at the same time requires the @code{\tweak} command.
+Bracket text will be parenthesized after a line break.
+
+"
+ doctitle = "Analysis brackets with labels"
+}
+
+\layout {
+ \context {
+ \Voice
+ \consists "Horizontal_bracket_engraver"
+ \override HorizontalBracket.direction = #UP
+ }
+}
+
+{
+ \once\override HorizontalBracketText.text = "a"
+ c''\startGroup d''\stopGroup
+ \once\override HorizontalBracketText.text = "a'"
+ e''\startGroup d''\stopGroup
+ c''
+ -\tweak text \markup \bold \huge "b" \startGroup
+ -\tweak text "a" \startGroup
+ d''\stopGroup
+ e''-\tweak text "a'" \startGroup
+ d''\stopGroup\stopGroup
+ c''-\tweak text foo \startGroup d'' e'' f''
+ \break
+ g'' a'' b'' c'''\stopGroup
+}
--- /dev/null
+\version "2.19.55"
+
+\header {
+ texidoc = "Text is parenthesized when analysis brackets cross line
+breaks.
+"
+}
+
+\layout {
+ \context {
+ \Voice
+ \consists "Horizontal_bracket_engraver"
+ }
+}
+
+{
+ c''
+ -\tweak text \markup \draw-circle #1 #0.5 ##f \startGroup
+ -\tweak text "a" \startGroup
+ d'' e'' f''
+ g'' a'' b'' c'''\stopGroup
+ c'''-\tweak text "a'" \startGroup b'' a'' g''
+ \break
+ f'' e'' d'' c''\stopGroup\stopGroup
+}
--- /dev/null
+\version "2.19.55"
+
+\header {
+ texidoc = "Labels may be added to analysis brackets through the
+@code{text} property of the @code{HorizontalBracketText} object. Use of
+the @code{\tweak} command is necessary for assigning text uniquely to
+brackets beginning at the same moment. Text assignments reflect the
+usual nesting order of brackets.
+"
+}
+
+\layout {
+ \context {
+ \Voice
+ \consists "Horizontal_bracket_engraver"
+ \override HorizontalBracket.direction = #UP
+ }
+}
+
+\relative c'' {
+ \time 3/4
+ \key f \major
+ c4
+ -\tweak text "contrasting period" \startGroup
+ -\tweak text "a" \startGroup
+ a8( bes c f)
+ f4( e d)
+ c d8( c bes c)
+ \appoggiatura bes4 a2 g4\stopGroup
+ \once\override HorizontalBracketText.text = "b"
+ f'8 \startGroup
+ r a, r d r
+ c4( e, f)
+ g8( bes) a4 g8( f)
+ f2 \stopGroup \stopGroup r4
+}
public:
TRANSLATOR_DECLARATIONS (Horizontal_bracket_engraver);
vector<Spanner *> bracket_stack_;
+ vector<Spanner *> text_stack_;
vector<Stream_event *> events_;
vsize pop_count_;
vsize push_count_;
ly_symbol2scm ("columns"), gi.grob ());
add_bound_item (bracket_stack_[i],
gi.grob ());
+ add_bound_item (text_stack_[i],
+ gi.grob ());
}
}
{
Spanner *sp = make_spanner ("HorizontalBracket", events_[k]->self_scm ());
+ Spanner *hbt = make_spanner ("HorizontalBracketText", events_[k]->self_scm ());
+
+ sp->set_object ("bracket-text", hbt->self_scm ());
+
+ Side_position_interface::add_support (hbt, sp);
+
+ hbt->set_parent (sp, X_AXIS);
+ hbt->set_parent (sp, Y_AXIS);
+ hbt->set_object ("bracket", sp->self_scm ());
+
for (vsize i = 0; i < bracket_stack_.size (); i++)
/* sp is the smallest, it should be added to the bigger brackets. */
- Side_position_interface::add_support (bracket_stack_[i], sp);
+ {
+ Side_position_interface::add_support (bracket_stack_[i], sp);
+ Side_position_interface::add_support (bracket_stack_[i], hbt);
+ }
+
bracket_stack_.push_back (sp);
+ text_stack_.push_back (hbt);
}
}
Horizontal_bracket_engraver::stop_translation_timestep ()
{
for (vsize i = pop_count_; i--;)
- if (bracket_stack_.size ())
- bracket_stack_.pop_back ();
+ {
+ if (bracket_stack_.size ())
+ bracket_stack_.pop_back ();
+ if (text_stack_.size ())
+ text_stack_.pop_back ();
+ }
pop_count_ = 0;
push_count_ = 0;
events_.clear ();
" purposes.",
/* create */
- "HorizontalBracket ",
+ "HorizontalBracket "
+ "HorizontalBracketText ",
/* read */
"",
}
}
-/*
- TODO:
-
- Support texts on the brackets?
-*/
MAKE_SCHEME_CALLBACK (Horizontal_bracket, print, 1);
SCM
Horizontal_bracket::print (SCM smob)
/* properties */
"bracket-flare "
+ "bracket-text "
"columns "
"edge-height "
"shorten-pair "
'(columns
common-shortest-duration))
+(ly:add-interface
+ 'horizontal-bracket-text-interface
+ "Label for an analysis bracket."
+ '(bracket columns))
+
(ly:add-interface
'inline-accidental-interface
"An inlined accidental (i.e. normal accidentals, cautionary
column as start/@/begin point. Only columns that have grobs or act as
bounds are spaced.")
(bracket ,ly:grob? "The bracket for a number.")
+ (bracket-text ,ly:grob? "The text for an analysis bracket.")
(c0-position ,integer? "An integer indicating the position of
middle@tie{}C.")
side-position-interface
spanner-interface))))))
+ (HorizontalBracketText
+ . (
+ (direction . ,ly:horizontal-bracket-text::calc-direction)
+ (font-size . -1)
+ (padding . 0.5)
+ (parent-alignment-X . ,CENTER)
+ (self-alignment-X . ,CENTER)
+ (side-axis . ,Y)
+ (stencil . ,ly:horizontal-bracket-text::print)
+ (X-offset . ,ly:self-alignment-interface::aligned-on-x-parent)
+ (Y-offset . ,side-position-interface::y-aligned-side)
+ (meta . ((class . Spanner)
+ (interfaces . (font-interface
+ horizontal-bracket-text-interface
+ outside-staff-interface
+ self-alignment-interface
+ side-position-interface
+ text-interface))))))
(InstrumentName
. (
X)))
num))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; HorizontalBracketText
+
+(define-public (ly:horizontal-bracket-text::print grob)
+ (let ((text (ly:grob-property grob 'text)))
+ (if (or (null? text)
+ (equal? text "")
+ (equal? text empty-markup))
+ (begin
+ (ly:grob-suicide! grob)
+ '())
+ (let* ((orig (ly:grob-original grob))
+ (siblings (ly:spanner-broken-into orig))
+ (text
+ (if (or (null? siblings)
+ (eq? grob (car siblings)))
+ text
+ (if (string? text)
+ (string-append "(" text ")")
+ (make-parenthesize-markup text)))))
+ (grob-interpret-markup grob text)))))
+
+(define-public (ly:horizontal-bracket-text::calc-direction grob)
+ (let* ((bracket (ly:grob-object grob 'bracket))
+ (bracket-dir (ly:grob-property bracket 'direction DOWN)))
+ bracket-dir))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; make-engraver helper macro