Source code for neoscore.western.brace

from typing import List, Optional, Union, cast

from neoscore.core.brush import BrushDef
from neoscore.core.layout_controllers import MarginController, NewLine
from neoscore.core.music_font import MusicFont, MusicFontGlyphNotFoundError
from neoscore.core.music_text import MusicText
from neoscore.core.pen import PenDef
from neoscore.core.point import Point
from neoscore.core.text_alignment import AlignmentX
from neoscore.core.units import ZERO, Unit
from neoscore.western.abstract_staff import AbstractStaff
from neoscore.western.multi_staff_object import MultiStaffObject
from neoscore.western.staff_group import StaffGroup


[docs]class Brace(MultiStaffObject, MusicText): """A brace spanning staves. This is drawn in the fringe of every staff system in the specified group. """
[docs] def __init__( self, staves: Union[StaffGroup, List[AbstractStaff]], font: Optional[MusicFont] = None, brush: Optional[BrushDef] = None, pen: Optional[PenDef] = None, ): """ Args: staves: The staves spanned. If a raw list of staves is given, it must be in descending order. font: If provided, this overrides the font in the parent (top) staff. brush: The brush to fill shapes with. pen: The pen to draw outlines with. """ MultiStaffObject.__init__(self, staves) # Calculate the height of the brace in highest_staff staff units scale = cast(float, self.vertical_span / self.highest.unit(4)) if self.vertical_span > self.highest.unit(50): text = ("brace", 4) elif self.vertical_span > self.highest.unit(30): text = ("brace", 3) elif self.vertical_span > self.highest.unit(15): text = ("brace", 2) elif self.vertical_span > self.highest.unit(4): text = "brace" else: text = ("brace", 1) try: # Attempt to use size-specific optional glyph MusicText.__init__( self, (ZERO, self.vertical_span), self.highest, text, font, brush, pen, scale, alignment_x=AlignmentX.RIGHT, ) except MusicFontGlyphNotFoundError: # Default to non-optional glyph MusicText.__init__( self, (ZERO, self.vertical_span), self.highest, "brace", font, brush, pen, scale, alignment_x=AlignmentX.RIGHT, )
@property def breakable_length(self) -> Unit: """This class's breakable length is that of its highest staff""" return self.highest.breakable_length def _render_occurrence( self, pos: Point, flowable_line: Optional[NewLine], ): fringe_layout = self.highest.fringe_layout_at(flowable_line) super().render_complete( Point(pos.x + fringe_layout.staff, pos.y), flowable_line )
[docs] def render_complete( self, pos: Point, flowable_line: Optional[NewLine] = None, flowable_x: Optional[Unit] = None, ): self._render_occurrence(pos, flowable_line)
[docs] def render_before_break(self, pos: Point, flowable_line: NewLine, flowable_x: Unit): self._render_occurrence(pos, flowable_line)
[docs] def render_spanning_continuation( self, pos: Point, flowable_line: NewLine, object_x: Unit ): self._render_occurrence(pos, flowable_line)
[docs] def render_after_break(self, pos: Point, flowable_line: NewLine, object_x: Unit): self._render_occurrence(pos, flowable_line)
def _register_layout_controllers(self): flowable = self.flowable if not flowable: return staff_flowable_x = flowable.descendant_pos_x(self.highest) flowable.add_margin_controller( MarginController( staff_flowable_x, self.bounding_rect.width, "_neoscore_brace", ) )
[docs] def pre_render_hook(self): super().pre_render_hook() self._register_layout_controllers()