Esempio n. 1
0
    def __init__(self, owner: AssociationItem, end: Optional[str] = None):
        self._canvas = None
        self._owner = owner
        self._end = end

        # Rendered text for name and multiplicity
        self._name = ""
        self._mult = ""

        self._name_bounds = Rectangle()
        self._mult_bounds = Rectangle()

        self._name_layout = Layout("")
        self._mult_layout = Layout("")

        self._inline_style: Style = {"font-size": 10}
Esempio n. 2
0
    def __init__(self, owner, end=None):
        super().__init__(id=False)  # Transient object
        self.canvas = None
        self._owner = owner
        self._end = end

        # Rendered text for name and multiplicity
        self._name = ""
        self._mult = ""

        self._name_bounds = Rectangle()
        self._mult_bounds = Rectangle()

        self._name_layout = Layout("")
        self._mult_layout = Layout("")

        self._inline_style: Style = {"font-size": 10}
Esempio n. 3
0
def test_text_with_font_as_dict_with_values_set_to_none():
    w, h = Layout(
        "Example",
        {
            "font-family": "sans",
            "font-size": 9.5,
            "font-style": None,
            "font-weight": FontWeight.BOLD,
            "text-decoration": TextDecoration.NONE,
        },
    ).size()
    assert w
    assert h
Esempio n. 4
0
def test_text_with_font_as_dict():
    w, h = Layout(
        "Example",
        {
            "font-family": "sans",
            "font-size": 10,
            "font-style": FontStyle.ITALIC,
            "font-weight": FontWeight.BOLD,
            "text-decoration": TextDecoration.UNDERLINE,
        },
    ).size()
    assert w
    assert h
Esempio n. 5
0
 def draw_partitions(self, bounding_box: Rectangle,
                     context: DrawContext) -> None:
     """Draw partition separators and add the name."""
     cr = context.cairo
     if self.partition:
         partition_width = bounding_box.width / len(self.partition)
     else:
         partition_width = bounding_box.width / 2
     layout = Layout()
     style = context.style
     padding_top = context.style["padding"][0]
     for num, partition in enumerate(self.partition):
         cr.move_to(partition_width * num, 0)
         cr.line_to(partition_width * num, bounding_box.height)
         layout.set(text=partition.name, font=style)
         cr.move_to(partition_width * num, padding_top * 3)
         layout.show_layout(
             cr,
             partition_width,
             default_size=(partition_width, HEADER_HEIGHT),
         )
Esempio n. 6
0
 def __init__(self, text=lambda: "", width=lambda: -1, style: Style = {}):
     self._text = text if callable(text) else lambda: text
     self.width = width if callable(width) else lambda: width
     self._inline_style = style
     self._layout = Layout()
Esempio n. 7
0
class AssociationEnd:
    """An association end represents one end of an association. An association
    has two ends. An association end has two labels: one for the name and one
    for the multiplicity (and maybe one for tagged values in the future).

    An AsociationEnd has no ID, hence it will not be stored, but it will
    be recreated by the owning Association.
    """

    def __init__(self, owner: AssociationItem, end: Optional[str] = None):
        self._canvas = None
        self._owner = owner
        self._end = end

        # Rendered text for name and multiplicity
        self._name = ""
        self._mult = ""

        self._name_bounds = Rectangle()
        self._mult_bounds = Rectangle()

        self._name_layout = Layout("")
        self._mult_layout = Layout("")

        self._inline_style: Style = {"font-size": 10}

    name_bounds = property(lambda s: s._name_bounds)

    @property
    def owner(self) -> AssociationItem:
        """Override Element.owner."""
        return self._owner

    @property
    def owner_handle(self) -> Handle:
        # handle(event) is the event handler method
        return self._owner.head if self is self._owner.head_end else self._owner.tail

    @property
    def subject(self) -> Optional[UML.Property]:
        return getattr(self.owner, f"{self._end}_subject")  # type:ignore[no-any-return]

    def request_update(self):
        self._owner.request_update()

    def set_text(self):
        """Set the text on the association end."""
        if self.subject:
            try:
                n, m = format_association_end(self.subject)
            except ValueError:
                # need more than 0 values to unpack: property was rendered as
                # attribute while in a UNDO action for example.
                pass
            else:
                self._name = n
                self._mult = m
                self.request_update()

    def get_name(self):
        return self._name

    def get_mult(self):
        return self._mult

    def post_update(self, context, p1, p2):
        """Update label placement for association's name and multiplicity
        label.

        p1 is the line end and p2 is the last but one point of the line.
        """
        style = combined_style(context.style, self._inline_style)
        ofs = 5

        dx = float(p2[0]) - float(p1[0])
        dy = float(p2[1]) - float(p1[1])

        def max_text_size(size1, size2):
            w1, h1 = size1
            w2, h2 = size2
            return (max(w1, w2), max(h1, h2))

        name_layout = self._name_layout
        name_layout.set_text(self._name)
        name_layout.set_font(style)
        name_w, name_h = max_text_size(name_layout.size(), (10, 10))

        mult_layout = self._mult_layout
        mult_layout.set_text(self._mult)
        mult_layout.set_font(style)
        mult_w, mult_h = max_text_size(mult_layout.size(), (10, 10))

        if dy == 0:
            rc = 1000.0  # quite a lot...
        else:
            rc = dx / dy
        abs_rc = abs(rc)
        h = dx > 0  # right side of the box
        v = dy > 0  # bottom side

        if abs_rc > 6:
            # horizontal line
            if h:
                name_dx = ofs
                name_dy = -ofs - name_h
                mult_dx = ofs
                mult_dy = ofs
            else:
                name_dx = -ofs - name_w
                name_dy = -ofs - name_h
                mult_dx = -ofs - mult_w
                mult_dy = ofs
        elif 0 <= abs_rc <= 0.2:
            # vertical line
            if v:
                name_dx = -ofs - name_w
                name_dy = ofs
                mult_dx = ofs
                mult_dy = ofs
            else:
                name_dx = -ofs - name_w
                name_dy = -ofs - name_h
                mult_dx = ofs
                mult_dy = -ofs - mult_h
        else:
            # Should both items be placed on the same side of the line?
            r = abs_rc < 1.0

            # Find out alignment of text (depends on the direction of the line)
            align_left = h ^ r
            align_bottom = v ^ r
            if align_left:
                name_dx = ofs
                mult_dx = ofs
            else:
                name_dx = -ofs - name_w
                mult_dx = -ofs - mult_w
            if align_bottom:
                name_dy = -ofs - name_h
                mult_dy = -ofs - name_h - mult_h
            else:
                name_dy = ofs
                mult_dy = ofs + mult_h

        self._name_bounds = Rectangle(
            p1[0] + name_dx, p1[1] + name_dy, width=name_w, height=name_h
        )

        self._mult_bounds = Rectangle(
            p1[0] + mult_dx, p1[1] + mult_dy, width=mult_w, height=mult_h
        )

    def point(self, x, y):
        """Given a point (x, y) return the distance to the diagram item."""
        drp = distance_rectangle_point
        pos = (x, y)
        d1 = drp(self._name_bounds, pos)
        d2 = drp(self._mult_bounds, pos)
        d3 = 1000.0
        return min(d1, d2, d3)

    def draw(self, context):
        """Draw name and multiplicity of the line end."""
        if not self.subject:
            return

        cr = context.cairo
        text_color = context.style.get("text-color")
        if text_color:
            cr.set_source_rgba(*text_color)

        cr.move_to(self._name_bounds.x, self._name_bounds.y)
        self._name_layout.show_layout(cr)
        cr.move_to(self._mult_bounds.x, self._mult_bounds.y)
        self._mult_layout.show_layout(cr)

        for b in (self._name_bounds, self._mult_bounds):
            text_draw_focus_box(context, b.x, b.y, b.width, b.height)
Esempio n. 8
0
def test_text_with_just_font_as_dict():
    w, h = Layout("Example", {"font-family": "sans", "font-size": 10}).size()
    assert w
    assert h