Пример #1
0
    def _highlight_border(self, context: cairo.Context, row: int, col: int):
        width = self._total_width
        height = self._total_height

        line_width = 3
        row_rectangles = [
            Rectangle(Point(1, row * self.cell_height - line_width / 2),
                      width - 2, line_width),
            Rectangle(Point(1, (row + 1) * self.cell_height - line_width / 2),
                      width - 2, line_width)
        ]
        col_rectangles = [
            Rectangle(Point(col * self.cell_width - line_width / 2, 1),
                      line_width, height - 2),
            Rectangle(Point((col + 1) * self.cell_width - line_width / 2, 1),
                      line_width, height - 2)
        ]
        context.save()
        r, g, b = self.highlight_color
        context.set_source_rgba(r, g, b, .6)
        for row_rectangle in row_rectangles:
            context.rectangle(row_rectangle.start.x, row_rectangle.start.y,
                              row_rectangle.width, row_rectangle.height)
            context.fill()
        for col_rectangle in col_rectangles:
            context.rectangle(col_rectangle.start.x, col_rectangle.start.y,
                              col_rectangle.width, col_rectangle.height)
            context.fill()
        context.restore()
Пример #2
0
 def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN):
     v = self._vertexes
     start = Point(min(x.x for x in v), min(x.y for x in v))
     width = max(x.x for x in v) - start.x
     height = max(x.y for x in v) - start.y
     rectangle = Rectangle(start, width, height)
     return rectangle.is_point_in(p)
Пример #3
0
 def is_point_in(self,p: Point,category=MouseEvent.UNKNOWN):
     v = self._vertexes
     start = Point(min(x.x for x in v), min(x.y for x in v))
     width = max(x.x for x in v) - start.x
     height = max(x.y for x in v) - start.y
     rectangle = Rectangle(start, width, height)
     return rectangle.is_point_in(p)
Пример #4
0
    def __init__(self, side=40, protrusion=30):
        super().__init__()
        base_size = side
        height = protrusion
        colour = global_constants.start_selected
        arrow_up = Arrow(base_size, height, Arrow.UP, colour)
        arrow_down = Arrow(base_size, height, Arrow.DOWN, colour)
        arrow_left = Arrow(base_size, height, Arrow.LEFT, colour)
        arrow_right = Arrow(base_size, height, Arrow.RIGHT, colour)
        factor=6
        center = height + base_size/2 + (base_size/factor)
        distance = (base_size + height)/2 + (base_size/factor)

        arrow_up.translate(center, center - distance)
        arrow_left.translate(center - distance, center)
        arrow_right.translate(center + distance, center)
        arrow_down.translate(center, center + distance)

        self.background_rectangle = Rectangle(Point(0,0), center * 2, center * 2)

        self.add(arrow_up)
        self.add(arrow_down)
        self.add(arrow_left)
        self.add(arrow_right)

        self._should_pass_move = False
Пример #5
0
    def layout(self, context: cairo.Context):
        super().layout(context)
        context.set_font_size(self.font_size)
        xb, yb, w, h, xa, ya = context.text_extents(self.title)
        font_shape = Rectangle(Point(h / 2 + self.distance, self.distance), xa,
                               h)
        self.__title_start_point = Point(font_shape.start.x,
                                         font_shape.start.y + h)
        outer_font_box = DrawableRectangle(
            Point(font_shape.start.x - self.distance,
                  font_shape.start.y - self.distance),
            font_shape.width + 2 * self.distance,
            font_shape.height + 2 * self.distance)
        self.__outer_font_box = outer_font_box
        wrapper_shape = DrawableRectangle(
            Point(0, outer_font_box.start.y + outer_font_box.height / 2),
            outer_font_box.start.x +
            max(outer_font_box.width, self.widget.shape.width) + self.distance,
            outer_font_box.height / 2 + 2 * self.distance +
            self.widget.shape.height)

        self.widget.set_translate(
            outer_font_box.start.x,
            outer_font_box.start.y + outer_font_box.height + self.distance)

        self.__wrapper_shape = wrapper_shape
        self.shape = DrawableRectangle(
            Point(0, 0), wrapper_shape.width,
            wrapper_shape.start.y + wrapper_shape.height)
Пример #6
0
 def __init__(self, width, height, background_color=None, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.width = width
     self.height = height
     self.font_size = 10
     self.clip_rectangle = Rectangle(Point(0, 0), width, height)
     self._text = ""
     self._background_color = background_color
Пример #7
0
 def get_cell(self, p: Point):
     delta = self.line_distance
     for index, element in enumerate(self._elements):
         if Rectangle(
                 Point(*element.position) + Point(-delta, delta),
                 element.width + 2 * delta,
                 -element.height - 2 * delta).is_point_in(p):
             return index
     return None
Пример #8
0
 def _update_selection_rectangle(self):
     if self._selection_start_point is not None and \
        self._selection_end_point is not None:
         s = self._selection_start_point
         e = self._selection_end_point
         upper = Point(min(s.col, e.col), min(s.row, e.row))
         lower = Point(max(s.col, e.col), max(s.row, e.row))
         height = lower.row - upper.row
         width = lower.col - upper.col
         self._selection_rectangle = Rectangle(upper, width, height)
Пример #9
0
    def layout(self, context: cairo.Context):
        super().layout(context)
        width, height = self.container_size
        self.__background_rectangle = DrawableRectangle(Point(0, 0), width, height)

        context.set_font_size(self.font_size)

        xb, yb, w, h, xa, ya = context.text_extents(self.label)
        padding = self.padding

        label_start = Point((width - xa)/2, (height - h)/2)
        self.__label_start = Point(label_start.x, label_start.y + h)
        label_shape = Rectangle(label_start, xa, h)

        back_shape = self.__back_button.shape
        ok_shape = self.__ok_button.shape

        button_y = label_start.y + label_shape.height + padding/2
        total_height = label_shape.height +\
                       max(back_shape.height, ok_shape.height) +\
                       1.5 * padding
        button_line_width = back_shape.width + ok_shape.width + 2*padding
        if button_line_width + 2*padding <= label_shape.width:
            self.__back_button.set_translate(
                label_start.x + padding,
                # (width - button_line_width)/2 + back_shape.width,
                button_y
            )
            self.__ok_button.set_translate(
                label_start.x + label_shape.width - padding,
                # (width + button_line_width)/2 - ok_shape.width,
                button_y
            )
            self.__overlay_shape = DrawableRectangle(
                Point(label_start.x - padding, label_start.y - padding/2),
                label_shape.width + 2 * padding,
                total_height
            )
        else:
            self.__back_button.set_translate(
                (width - button_line_width)/2,
                button_y
            )
            self.__ok_button.set_translate(
                (width + button_line_width)/2,
                button_y
            )
            self.__overlay_shape = DrawableRectangle(
                Point((width - button_line_width)/2 - padding,
                      label_start.y - padding/2),
                2*padding + button_line_width,
                total_height
            )
Пример #10
0
    def _get_text_max_size(self, context, lines):
        sizes = []
        for l in lines:
            xb, yb, width, height, _, _ = context.text_extents(l)
            sizes.append(Rectangle(Point(xb, yb), width, height))

        maxW, maxH = 0, 0
        for s in sizes:
            maxW = max(maxW, s.width - s.start.x)
            maxH = max(maxH, s.height - s.start.y)

        return maxH, maxW
Пример #11
0
    def _highlight_rectangles(self, context, row: int, col: int):

        self._highlight_border(context, row, col)

        width = self._total_width
        height = self._total_height

        row_rectangle = Rectangle(Point(col * self.cell_width, 0),
                                  self.cell_width, height)
        col_rectangle = Rectangle(Point(0, row * self.cell_height), width,
                                  self.cell_height)
        r, g, b = self.highlight_color
        context.save()
        context.set_source_rgba(r, g, b, .1)
        context.rectangle(row_rectangle.start.x, row_rectangle.start.y,
                          row_rectangle.width, row_rectangle.height)
        context.fill()
        context.rectangle(col_rectangle.start.x, col_rectangle.start.y,
                          col_rectangle.width, col_rectangle.height)
        context.fill()
        context.restore()
Пример #12
0
 def _update_cell_list(self):
     """Should be called only after on_draw has been called at least once"""
     self._cell_list = []
     for (line_index, element_list) in enumerate(self.elements):
         line = self._line_coordinates(line_index)
         if self.orientation == Guides.HORIZONTAL:
             next_x = line[0].x - self.font_size // 2
             next_y = line[0].y - (2 * self.font_size) // 3
         elif self.orientation == Guides.VERTICAL:
             next_x = line[0].x - self.font_size // 2
             next_y = line[0].y - self.font_size // 2
         total_length = 0
         #self.line_length = max_numbers * (self._number_height + self.font_size // 2) + 5
         for element in reversed(element_list):
             text = str(element.value)
             if self.orientation == Guides.HORIZONTAL:
                 width = self._number_width * len(text)
                 height = self._number_height
                 rectangle = Rectangle(Point(next_x - width, next_y), width,
                                       -height)
                 wide_rectangle = Rectangle(Point(line[0].x, next_y),
                                            -self.cell_size, -height)
                 next_y = next_y - height - self.font_size // 2
                 total_length += height + self.font_size // 2
             elif self.orientation == Guides.VERTICAL:
                 width = self._number_width * (len(text) + 1)
                 height = self._number_height
                 rectangle = Rectangle(Point(next_x - width, next_y), width,
                                       -height)
                 wide_rectangle = Rectangle(
                     Point(next_x - width, line[0].y), width,
                     -self.cell_size)
                 next_x -= width + self.font_size // 2
                 total_length += width + self.font_size // 2
             element.cell = rectangle
             element.wide_cell = wide_rectangle
         self.line_length = max(total_length + 5, self.line_length)
     self._update_clip()
Пример #13
0
    def on_draw(self, widget, context):

        context.save()
        context.set_line_width(1)
        width = self._total_width
        height = self._total_height

        for row in range(self.number_of_rows):
            for col in range(self.number_of_cols):
                value = self.get_cell_value(row, col)
                rectangle = Rectangle(
                    Point(col * self.cell_width, row * self.cell_height),
                    self.cell_width + 2, self.cell_height + 2)
                self._draw_cell(context, value, rectangle)

        context.set_source_rgb(0, 0, 0)
        context.set_line_cap(cairo.LINE_CAP_SQUARE)

        context.set_line_width(2.5)
        DrawableRectangle(Point(0, 0), width, height).draw_on_context(context)
        context.stroke()

        if not self.victory_screen:
            for i, x in enumerate(
                    range(0, width + self.cell_width, self.cell_width)):
                if i % 5 == 0:
                    context.set_line_width(2.5)
                else:
                    context.set_line_width(1)
                context.move_to(x, 0)
                context.line_to(x, height)
                context.stroke()

            for i, y in enumerate(
                    range(0, height + self.cell_height, self.cell_height)):
                if i % 5 == 0:
                    context.set_line_width(2.5)
                else:
                    context.set_line_width(1)
                context.move_to(0, y)
                context.line_to(width, y)
                context.stroke()

        if self._highlight_col is not None and self._highlight_row is not None\
                and self._should_highlight and not self.victory_screen:
            self._highlight_rectangles(context, self._highlight_row,
                                       self._highlight_col)

        context.restore()
Пример #14
0
 def clip_rectangle(self):
     if super().is_clip_set():
         return super().clip_rectangle
     else:
         vertexes = []
         for child in self.list:
             if child.is_clip_set():
                 child_vertexes = [Point(child.fromWidgetCoords.transform_point(p.x,p.y))\
                                     for p in child.clip_rectangle.get_vertexes()]
                 vertexes.extend(child_vertexes)
             else:
                 return super().clip_rectangle
         if not vertexes:
             return super().clip_rectangle
         else:
             return Rectangle.from_points(vertexes)
Пример #15
0
 def clip_rectangle(self):
     if super().is_clip_set():
         return super().clip_rectangle
     else:
         vertexes = []
         for child in self.list:
             if child.is_clip_set():
                 child_vertexes = [Point(child.fromWidgetCoords.transform_point(p.x,p.y))\
                                     for p in child.clip_rectangle.get_vertexes()]
                 vertexes.extend(child_vertexes)
             else:
                 return super().clip_rectangle
         if not vertexes:
             return super().clip_rectangle
         else:
             return Rectangle.from_points(vertexes)
Пример #16
0
    def set_shape_from_context(self, context: cairo.Context):

        widths_heights = []

        done = False

        while not done:
            max_width = 0
            max_height = 0
            context.set_font_size(self.font_size)

            for e in reversed(self._elements):
                xb, yb, w, h, xa, ya = context.text_extents(e.label)
                widths_heights.append((xa, h))
                e.width = xa
                e.height = h
                max_width = max(max_width, xa)
                max_height = max(max_height, h)

            # adjust font size in case it's too big
            if self.max_size is None:
                done = True
            else:
                if self.orientation == Orientation.HORIZONTAL:
                    reference = max_height
                else:
                    reference = max_width
                if reference + 2 * self.line_distance <= self.max_size:
                    done = True
                else:
                    self.font_size -= 1

        positions = []
        width = self.element_distance
        height = self.element_distance

        def get_padding(actual_size):
            if self.max_size is not None:
                return (self.max_size - actual_size) / 2
            else:
                return self.line_distance

        if self.orientation == Orientation.HORIZONTAL:

            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_height
                width += e.width
                e.position = (-width, max_height + get_padding(max_height))
                width += self.element_distance
        else:

            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_width
                e.position = (get_padding(e.width), -height)
                height += e.height + self.element_distance

        # for w, h in widths_heights:
        #     handle_extents(w, h)

        for element in reversed(self._elements):
            handle_extents(element)

        if self.orientation == Orientation.HORIZONTAL:
            height = max_height + get_padding(max_height) * 2
            width = width - self.element_distance + self.line_distance
            base_point = Point(-width, 0)
        else:
            width = max_width + get_padding(max_width) * 2
            height = height - self.element_distance + self.line_distance
            base_point = Point(0, -height)

        self.shape = Rectangle(base_point, width, height)
Пример #17
0
    def __init__(self,
                 crucipixel: core.Crucipixel,
                 cell_width: int = 20,
                 cell_height: int = 20,
                 **kwargs):
        super().__init__(**kwargs)

        self.crucipixel = crucipixel
        self.cell_width = cell_width
        self.cell_height = cell_height

        self.clip_rectangle = Rectangle(Point(-1, -1), self._total_width + 2,
                                        self._total_height + 2)

        self.highlight_color = global_constants.highlight

        self._cell_value_order = [
            CrucipixelCellValue.SELECTED, CrucipixelCellValue.EMPTY,
            CrucipixelCellValue.DEFAULT
        ]

        self.crucipixel_cell_value_to_color = {
            CrucipixelCellValue.EMPTY: global_constants.start_empty,
            CrucipixelCellValue.DEFAULT: global_constants.start_default,
            CrucipixelCellValue.SELECTED: global_constants.start_selected
        }

        self._mouse_button_to_crucipixel_cell_value = {
            MouseButton.LEFT: CrucipixelCellValue.SELECTED,
            MouseButton.RIGHT: CrucipixelCellValue.EMPTY,
            MouseButton.MIDDLE: CrucipixelCellValue.DEFAULT
        }

        self._keyboard_value_to_crucipixel_cell_value = {
            'space': CrucipixelCellValue.SELECTED,
            'enter': CrucipixelCellValue.EMPTY,
            'backspace': CrucipixelCellValue.DEFAULT,
            'z': CrucipixelCellValue.SELECTED,
            'x': CrucipixelCellValue.EMPTY,
            'c': CrucipixelCellValue.DEFAULT,
            'i': CrucipixelCellValue.SELECTED,
            'o': CrucipixelCellValue.EMPTY,
            'p': CrucipixelCellValue.DEFAULT,
        }

        self._keyboard_value_to_movement_directions = {
            'w': (Direction.UP, ),
            'a': (Direction.LEFT, ),
            's': (Direction.DOWN, ),
            'd': (Direction.RIGHT, ),
            'k': (Direction.UP, ),
            'h': (Direction.LEFT, ),
            'j': (Direction.DOWN, ),
            'l': (Direction.RIGHT, ),
            'y': (Direction.UP, Direction.LEFT),
            'u': (Direction.UP, Direction.RIGHT),
            'b': (Direction.DOWN, Direction.LEFT),
            'n': (Direction.DOWN, Direction.RIGHT)
        }

        self._key_pressed = set()

        self._should_highlight = False
        self._highlight_row = None
        self._highlight_col = None

        self.victory_screen = False

        self._selection_value = None
        self._selection_start_point = None
        self._selection_end_point = None
        self._selection_rectangle = None
        self.is_destroyed = False

        def default_guide_update(orientation: Orientation, line: int,
                                 status: GuideStatus):
            pass

        self.on_guide_update = default_guide_update

        def timeout_function() -> bool:
            self.invalidate()
            return not self.is_destroyed

        GLib.timeout_add(33, timeout_function)
Пример #18
0
 def shape(self):
     w, h = self.container_size
     return Rectangle(Point(0, 0), w, h)
Пример #19
0
class _GuideLine(Widget):
    def __init__(self,
                 elements: List[int],
                 orientation: Orientation,
                 line_thickness: int,
                 font_size: int,
                 max_size: int = None,
                 element_distance: int = 10,
                 line_distance: int = 1.8,
                 **kwargs):
        super().__init__(**kwargs)

        if not elements:
            elements = [0]
        self._elements = [_GuideElement(str(e)) for e in elements]
        self.orientation = orientation
        self.line_thickness = line_thickness
        self.font_size = font_size
        self.shape = None
        self.max_size = max_size
        self.element_distance = element_distance
        self.line_distance = line_distance
        self.mouse_was_down = False

        self.line_extension = 0
        self._selected_cell = None

    def set_done(self):
        for e in self._elements:
            e.is_done = True
            e.is_wrong = False

    def set_wrong(self):
        for e in self._elements:
            e.is_wrong = True
            e.is_done = False

    def set_default(self):
        for e in self._elements:
            e.is_wrong = False
            e.is_done = False

    def set_cancelled(self, is_it: bool, index: int):
        self._elements[index].is_cancelled = is_it

    def toggle_cancelled(self, index: int):
        e = self._elements[index]
        e.is_cancelled = not e.is_cancelled

    def set_shape_from_context(self, context: cairo.Context):

        widths_heights = []

        done = False

        while not done:
            max_width = 0
            max_height = 0
            context.set_font_size(self.font_size)

            for e in reversed(self._elements):
                xb, yb, w, h, xa, ya = context.text_extents(e.label)
                widths_heights.append((xa, h))
                e.width = xa
                e.height = h
                max_width = max(max_width, xa)
                max_height = max(max_height, h)

            # adjust font size in case it's too big
            if self.max_size is None:
                done = True
            else:
                if self.orientation == Orientation.HORIZONTAL:
                    reference = max_height
                else:
                    reference = max_width
                if reference + 2 * self.line_distance <= self.max_size:
                    done = True
                else:
                    self.font_size -= 1

        positions = []
        width = self.element_distance
        height = self.element_distance

        def get_padding(actual_size):
            if self.max_size is not None:
                return (self.max_size - actual_size) / 2
            else:
                return self.line_distance

        if self.orientation == Orientation.HORIZONTAL:

            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_height
                width += e.width
                e.position = (-width, max_height + get_padding(max_height))
                width += self.element_distance
        else:

            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_width
                e.position = (get_padding(e.width), -height)
                height += e.height + self.element_distance

        # for w, h in widths_heights:
        #     handle_extents(w, h)

        for element in reversed(self._elements):
            handle_extents(element)

        if self.orientation == Orientation.HORIZONTAL:
            height = max_height + get_padding(max_height) * 2
            width = width - self.element_distance + self.line_distance
            base_point = Point(-width, 0)
        else:
            width = max_width + get_padding(max_width) * 2
            height = height - self.element_distance + self.line_distance
            base_point = Point(0, -height)

        self.shape = Rectangle(base_point, width, height)
        # for e, p in zip(self._elements, reversed(positions)):
        #     e.position = p

    def on_draw(self, widget: Widget, context: cairo.Context):

        self.set_shape_from_context(context)
        shape = self.shape

        context.set_line_width(self.line_thickness)
        if self.orientation == Orientation.HORIZONTAL:
            context.move_to(shape.start.x - self.line_extension, shape.start.y)
            context.line_to(shape.start.x + shape.width, shape.start.y)
        else:
            context.move_to(shape.start.x, shape.start.y - self.line_extension)
            context.line_to(shape.start.x, shape.start.y + shape.height)
        context.stroke()

        for element in self._elements:
            context.move_to(*element.position)
            context.set_source_rgb(*element.color)
            context.show_text(element.label)

    def get_cell(self, p: Point):
        delta = self.line_distance
        for index, element in enumerate(self._elements):
            if Rectangle(
                    Point(*element.position) + Point(-delta, delta),
                    element.width + 2 * delta,
                    -element.height - 2 * delta).is_point_in(p):
                return index
        return None

    def on_mouse_down(self, widget: Widget, event: MouseEvent):
        self._selected_cell = self.get_cell(event)

    def on_mouse_up(self, widget: Widget, event: MouseEvent):
        if self._selected_cell is not None and \
                        self._selected_cell == self.get_cell(event):
            self.toggle_cancelled(self._selected_cell)
            self.invalidate()
        self._selected_cell = None

    def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN):
        if self.shape is None:
            return False
        elif (category == MouseEvent.MOUSE_UP and self.mouse_was_down) \
                or self.shape.is_point_in(p):
            return True
        else:
            return False
Пример #20
0
 def shape(self) -> Rectangle:
     return Rectangle(Point(0, 0), self._total_width, self._total_height)
Пример #21
0
class Navigator(UncheckedContainer):

    def __init__(self, side=40, protrusion=30):
        super().__init__()
        base_size = side
        height = protrusion
        colour = global_constants.start_selected
        arrow_up = Arrow(base_size, height, Arrow.UP, colour)
        arrow_down = Arrow(base_size, height, Arrow.DOWN, colour)
        arrow_left = Arrow(base_size, height, Arrow.LEFT, colour)
        arrow_right = Arrow(base_size, height, Arrow.RIGHT, colour)
        factor=6
        center = height + base_size/2 + (base_size/factor)
        distance = (base_size + height)/2 + (base_size/factor)

        arrow_up.translate(center, center - distance)
        arrow_left.translate(center - distance, center)
        arrow_right.translate(center + distance, center)
        arrow_down.translate(center, center + distance)

        self.background_rectangle = Rectangle(Point(0,0), center * 2, center * 2)

        self.add(arrow_up)
        self.add(arrow_down)
        self.add(arrow_left)
        self.add(arrow_right)

        self._should_pass_move = False

    def on_mouse_up(self, widget, event):
        self._should_pass_move = False
        super().on_mouse_up(widget, event)
        return False

    def on_mouse_enter(self) -> bool:
        if self.mouse_is_down:
            self._should_pass_move = True
        return super().on_mouse_enter()

    def on_mouse_exit(self) -> bool:
        self._should_pass_move = False
        return super().on_mouse_exit()

    def on_mouse_move(self, widget, event):
        super().on_mouse_move(widget, event)
        return False

    def on_mouse_down(self, widget, event):
        super().on_mouse_down(widget, event)
        return True

    @property
    def fromWidgetCoords(self):
        width = self.background_rectangle.width
        height = self.background_rectangle.height
        total_width, total_height = self.container_size
        transl_width, transl_height = self._fromScale.transform_point(width, height)
        self.set_translate(total_width - transl_width, total_height - transl_height)
        return super().fromWidgetCoords

    @property
    def width(self):
        return self.background_rectangle.width

    def on_draw(self,widget,context):

        start = self.background_rectangle.start
        width = self.background_rectangle.width
        height = self.background_rectangle.height

        context.save()
        context.rectangle(start.x,start.y,width,height)
        context.set_source_rgb(0,0,0)
        context.stroke_preserve()
        context.set_source_rgb(*global_constants.background)
        context.fill()
        context.restore()

        super().on_draw(widget,context)

    def is_point_in(self, p:"Point", category=MouseEvent.UNKNOWN):
        return self.background_rectangle.is_point_in(p)
Пример #22
0
 def clip_rectangle(self):
     if not self.is_clip_set():
         raise AttributeError("Clip rectangle not set")
     return Rectangle(self._clip_start, self._clip_width, self._clip_height)
Пример #23
0
    def set_shape_from_context(self, context: cairo.Context):

        widths_heights = []

        done = False

        while not done:
            max_width = 0
            max_height = 0
            context.set_font_size(self.font_size)

            for e in reversed(self._elements):
                xb, yb, w, h, xa, ya = context.text_extents(e.label)
                widths_heights.append((xa, h))
                e.width = xa
                e.height = h
                max_width = max(max_width, xa)
                max_height = max(max_height, h)


            # adjust font size in case it's too big
            if self.max_size is None:
                done = True
            else:
                if self.orientation == Orientation.HORIZONTAL:
                    reference = max_height
                else:
                    reference = max_width
                if reference + 2 * self.line_distance <= self.max_size:
                    done = True
                else:
                    self.font_size -= 1

        positions = []
        width = self.element_distance
        height = self.element_distance

        def get_padding(actual_size):
            if self.max_size is not None:
                return (self.max_size - actual_size) / 2
            else:
                return self.line_distance

        if self.orientation == Orientation.HORIZONTAL:
            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_height
                width += e.width
                e.position = (-width,
                              max_height + get_padding(max_height))
                width += self.element_distance
        else:
            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_width
                e.position = (get_padding(e.width),
                              -height)
                height += e.height + self.element_distance

        # for w, h in widths_heights:
        #     handle_extents(w, h)

        for element in reversed(self._elements):
            handle_extents(element)

        if self.orientation == Orientation.HORIZONTAL:
            height = max_height + get_padding(max_height) * 2
            width = width - self.element_distance + self.line_distance
            base_point = Point(-width, 0)
        else:
            width = max_width + get_padding(max_width) * 2
            height = height - self.element_distance + self.line_distance
            base_point = Point(0, -height)

        self.shape = Rectangle(base_point, width, height)
Пример #24
0
class _GuideLine(Widget):

    def __init__(self, elements: List[int],
                 orientation: Orientation, line_thickness: int,
                 font_size: int, max_size: int = None,
                 element_distance: int=10, line_distance: int=1.8,
                 **kwargs):
        super().__init__(**kwargs)

        if not elements:
            elements = [0]
        self._elements = [_GuideElement(str(e)) for e in elements]
        self.orientation = orientation
        self.line_thickness = line_thickness
        self.font_size = font_size
        self.shape = None
        self.max_size = max_size
        self.element_distance = element_distance
        self.line_distance = line_distance
        self.mouse_was_down = False

        self.line_extension = 0
        self._selected_cell = None

    def set_done(self):
        for e in self._elements:
            e.is_done = True
            e.is_wrong = False

    def set_wrong(self):
        for e in self._elements:
            e.is_wrong = True
            e.is_done = False

    def set_default(self):
        for e in self._elements:
            e.is_wrong = False
            e.is_done = False

    def set_cancelled(self, is_it: bool, index: int):
        self._elements[index].is_cancelled = is_it

    def toggle_cancelled(self, index: int):
        e = self._elements[index]
        e.is_cancelled = not e.is_cancelled

    def set_shape_from_context(self, context: cairo.Context):

        widths_heights = []

        done = False

        while not done:
            max_width = 0
            max_height = 0
            context.set_font_size(self.font_size)

            for e in reversed(self._elements):
                xb, yb, w, h, xa, ya = context.text_extents(e.label)
                widths_heights.append((xa, h))
                e.width = xa
                e.height = h
                max_width = max(max_width, xa)
                max_height = max(max_height, h)


            # adjust font size in case it's too big
            if self.max_size is None:
                done = True
            else:
                if self.orientation == Orientation.HORIZONTAL:
                    reference = max_height
                else:
                    reference = max_width
                if reference + 2 * self.line_distance <= self.max_size:
                    done = True
                else:
                    self.font_size -= 1

        positions = []
        width = self.element_distance
        height = self.element_distance

        def get_padding(actual_size):
            if self.max_size is not None:
                return (self.max_size - actual_size) / 2
            else:
                return self.line_distance

        if self.orientation == Orientation.HORIZONTAL:
            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_height
                width += e.width
                e.position = (-width,
                              max_height + get_padding(max_height))
                width += self.element_distance
        else:
            def handle_extents(e: _GuideElement):
                nonlocal width, height, positions, max_width
                e.position = (get_padding(e.width),
                              -height)
                height += e.height + self.element_distance

        # for w, h in widths_heights:
        #     handle_extents(w, h)

        for element in reversed(self._elements):
            handle_extents(element)

        if self.orientation == Orientation.HORIZONTAL:
            height = max_height + get_padding(max_height) * 2
            width = width - self.element_distance + self.line_distance
            base_point = Point(-width, 0)
        else:
            width = max_width + get_padding(max_width) * 2
            height = height - self.element_distance + self.line_distance
            base_point = Point(0, -height)

        self.shape = Rectangle(base_point, width, height)
        # for e, p in zip(self._elements, reversed(positions)):
        #     e.position = p

    def on_draw(self, widget: Widget, context: cairo.Context):

        self.set_shape_from_context(context)
        shape = self.shape

        context.set_line_width(self.line_thickness)
        if self.orientation == Orientation.HORIZONTAL:
            context.move_to(shape.start.x - self.line_extension, shape.start.y)
            context.line_to(shape.start.x + shape.width, shape.start.y)
        else:
            context.move_to(shape.start.x, shape.start.y - self.line_extension)
            context.line_to(shape.start.x, shape.start.y + shape.height)
        context.stroke()

        for element in self._elements:
            context.move_to(*element.position)
            context.set_source_rgb(*element.color)
            context.show_text(element.label)

    def get_cell(self, p: Point):
        delta = self.line_distance
        for index, element in enumerate(self._elements):
            if Rectangle(Point(*element.position) + Point(-delta, delta),
                         element.width + 2 * delta,
                         -element.height - 2 * delta).is_point_in(p):
                return index
        return None

    def on_mouse_down(self, widget: Widget, event: MouseEvent):
        self._selected_cell = self.get_cell(event)

    def on_mouse_up(self, widget: Widget, event: MouseEvent):
        if self._selected_cell is not None and \
                        self._selected_cell == self.get_cell(event):
            self.toggle_cancelled(self._selected_cell)
            self.invalidate()
        self._selected_cell = None

    def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN):
        if self.shape is None:
            return False
        elif (category == MouseEvent.MOUSE_UP and self.mouse_was_down) \
                or self.shape.is_point_in(p):
            return True
        else:
            return False