Пример #1
0
    def __init__(self, height=40, width=None, padding=None, distance=None):
        super().__init__()

        if width is None:
            # width = (height / 15) * 7
            width = (height / 5) * 3
        if distance is None:
            distance = height / 5
        if padding is None:
            padding = height / 8

        arrow_width = width - 2 * padding
        arrow_height = (height - 2 * padding - distance) / 2
        self.up_arrow = Arrow(arrow_width, arrow_height, direction=Arrow.UP)
        self.down_arrow = Arrow(arrow_width,
                                arrow_height,
                                direction=Arrow.DOWN)

        self.up_arrow.translate(arrow_width / 2 + padding,
                                padding + arrow_height / 2)
        self.down_arrow.translate(arrow_width / 2 + padding,
                                  height - padding - arrow_height / 2)

        self.add(self.up_arrow)
        self.add(self.down_arrow)
        self.shape = DrawableRectangle(Point(0, 0), width, height)

        self.increment_action = lambda: None
        self.decrement_action = lambda: None

        self.__moving_action = None
        self.__skip = 0
Пример #2
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)
Пример #3
0
    def _handle_layout(self):
        start_y = self.padding
        for button in self._buttons:
            button.set_translate(self.padding, start_y)
            start_y += self.size + self.padding

        self.bounding_rectangle = DrawableRectangle(
            Point(0, 0),
            self.padding * 2 + self.size,
            start_y
        )

        self.shape = self.bounding_rectangle
Пример #4
0
    def __init__(self, height=40, width=None, padding=None, distance=None):
        super().__init__()

        if width is None:
            # width = (height / 15) * 7
            width = (height / 5) * 3
        if distance is None:
            distance = height / 5
        if padding is None:
            padding = height / 8

        arrow_width = width - 2 * padding
        arrow_height = (height - 2 * padding - distance) / 2
        self.up_arrow = Arrow(arrow_width,
                              arrow_height,
                              direction=Arrow.UP)
        self.down_arrow = Arrow(arrow_width,
                                arrow_height,
                                direction=Arrow.DOWN)

        self.up_arrow.translate(arrow_width / 2 + padding,
                                padding + arrow_height / 2)
        self.down_arrow.translate(arrow_width / 2 + padding,
                                  height - padding - arrow_height/2)

        self.add(self.up_arrow)
        self.add(self.down_arrow)
        self.shape = DrawableRectangle(Point(0, 0), width, height)

        self.increment_action = lambda: None
        self.decrement_action = lambda: None

        self.__moving_action = None
        self.__skip = 0
Пример #5
0
    def layout(self, context: cairo.Context):
        if not self.is_shape_set:
            context.save()
            context.set_font_size(self.font_size)
            generator = (context.text_extents(chr(letter) * 10)
                         for letter in range(ord('A'),
                                             ord('Z') + 1))
            context.restore()
            sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
            max_height = 0
            for w, h in sizes:
                max_height = max(h, max_height)

            width = self.width
            height = self.padding * 3 + max_height

            self.shape = DrawableRectangle(Point(0, 0), width, height)
Пример #6
0
    def layout(self, context: cairo.Context):
        context.save()
        context.set_font_size(self.font_size)
        generator = (context.text_extents(str(digit) * self.digits)
                     for digit in range(10))
        sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
        max_width = 0
        max_height = 0
        for w, h in sizes:
            max_width = max(w, max_width)
            max_height = max(h, max_height)

        width = self.padding * 2 + max_width
        height = self.padding * 2 + max_height

        self.shape = DrawableRectangle(Point(0, 0), width, height)
        context.restore()
Пример #7
0
    def layout(self, context: cairo.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

        rectangle_width = xa + 2 * padding
        rectangle_height = padding + h
        start_x = (width - rectangle_width) / 2
        start_y = (height - rectangle_height) / 2

        self.__label_rectangle = DrawableRectangle(Point(start_x, start_y),
                                                   rectangle_width,
                                                   rectangle_height)
Пример #8
0
    def layout(self, context: cairo.Context):
        if self.father is None:
            return
        width = self.father.container_size[0] - 2 * self.padding

        self._set_font(context)

        def fit_check(l: List[str]) -> bool:
            l = " ".join(l)
            xb, yb, w, h, xa, ya = context.text_extents(l)
            if xa > width:
                return False
            else:
                return True

        self.__text_lines = []
        start_y = self.padding / 2

        def append_line(line: str):
            nonlocal start_y
            if not line:
                start_y += self.padding
            else:
                _, yb, _, h, _, _ = context.text_extents(line)
                start_y += h - (yb * .75)
                self.__text_lines.append(
                    _TextLine(line, Point(self.padding, start_y)))
                # context.move_to(self.padding, start_y + h + yb)
                # context.show_text(line)

        for paragraph in self.text:
            words = paragraph.split(" ")
            start = 0
            while start < len(words):
                pivot = start
                end = len(words)
                if fit_check(islice(words, start, end)):
                    append_line(" ".join(islice(words, start, end)))
                    start = len(words)
                elif not fit_check([words[start]]):
                    append_line(words[start])
                    start += 1
                else:
                    while end > pivot + 1:
                        t = (end + pivot) // 2
                        if not fit_check(islice(words, start, t)):
                            end = t
                        else:
                            pivot = t
                    append_line(" ".join(islice(words, start, pivot)))
                    start = pivot
            append_line("")

            self.shape = DrawableRectangle(Point(0,
                                                 0), width + 2 * self.padding,
                                           start_y + self.padding / 2)
Пример #9
0
    def layout(self, context: cairo.Context):
        super().layout(context)

        padding = 20
        self.__content.set_translate(padding, self.__title.shape.height + padding/10)

        self.shape = DrawableRectangle(
            Point(0, 0),
            self.container_size[0],
            self.__title.shape.height + self.__content.shape.height + padding
        )
Пример #10
0
 def layout(self, context: cairo.Context):
     super().layout(context)
     if self.increment_decrement is None:
         self.increment_decrement = IncrementDecrement(
             height=self.number.shape.height)
         self.increment_decrement.set_translate(self.number.shape.width, 0)
         self.add(self.increment_decrement)
         self._select_actions()
         self.shape = DrawableRectangle(
             Point(0, 0),
             self.number.shape.width + self.increment_decrement.shape.width,
             self.number.shape.height)
Пример #11
0
    def layout(self, context: cairo.Context):

        super().layout(context)

        padding = 20


        current_y = self.__title.shape.height + padding
        for s in self.__sections:
            s.set_translate(0, current_y)
            current_y += padding + s.shape.height

        self.shape = DrawableRectangle(Point(0, 0), self.__title.shape.width,
                                       current_y - padding)
Пример #12
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()
Пример #13
0
    def layout(self, context: cairo.Context):
        super().layout(context)
        context.save()
        self.__plus_button.layout(context)
        context.restore()
        self.__minus_button.min_height = self.__plus_button.shape.height
        context.save()
        self.__minus_button.layout(context)
        context.restore()

        plus_shape = self.__plus_button.shape
        minus_shape = self.__minus_button.shape

        self.__minus_button.set_translate(self.padding, self.padding/2)
        self.__plus_button.set_translate(
            self.padding + minus_shape.width + self.padding,
            self.padding/2
        )

        self.shape = DrawableRectangle(
            Point(0, 0),
            plus_shape.width + minus_shape.width + 3 * self.padding,
            self.padding + max(plus_shape.height, minus_shape.height)
        )
Пример #14
0
    def layout(self, context: cairo.Context):
        if not self.is_shape_set:
            context.save()
            context.set_font_size(self.font_size)
            generator = (context.text_extents(chr(letter) * 10)
                         for letter in range(ord('A'), ord('Z') + 1))
            context.restore()
            sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
            max_height = 0
            for w, h in sizes:
                max_height = max(h, max_height)

            width = self.width
            height = self.padding * 3 + max_height

            self.shape = DrawableRectangle(Point(0, 0), width, height)
Пример #15
0
    def layout(self, context: cairo.Context):
        context.save()
        context.set_font_size(self.font_size)
        generator = (context.text_extents(str(digit) * self.digits)
                     for digit in range(10))
        sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
        max_width = 0
        max_height = 0
        for w, h in sizes:
            max_width = max(w, max_width)
            max_height = max(h, max_height)

        width = self.padding * 2 + max_width
        height = self.padding * 2 + max_height

        self.shape = DrawableRectangle(Point(0, 0), width, height)
        context.restore()
Пример #16
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
            )
Пример #17
0
    def layout(self, context: cairo.Context):
        super().layout(context)
        container_width = super().container_size[0]
        rows_shape = self.__wrapped_rows.shape
        cols_shape = self.__wrapped_cols.shape
        hard_shape = self.__wrapped_hard.shape
        text_shape = self.__wrapped_text.shape
        distance = max(self.rows_cols_distance,
                       (container_width - rows_shape.width\
                                        - hard_shape.width\
                                        - cols_shape.width)/2)
        offset_x = rows_shape.width + distance
        # offset_x = rows_shape.width + max(
        #     self.rows_cols_distance,
        #     container_width - rows_shape.width - cols_shape.width
        # )

        self.__wrapped_hard.set_translate(offset_x, 0)
        offset_x += hard_shape.width + distance

        self.__wrapped_cols.set_translate(offset_x, 0)
        self.__width = offset_x + cols_shape.width
        wrapped_text_start = rows_shape.height + self.rows_cols_distance
        self.__wrapped_text.set_translate(0, wrapped_text_start)
        buttons_start = wrapped_text_start + self.__wrapped_text.shape.height + 10

        self.__back_button.set_translate(10, buttons_start)

        self.__create_button.set_translate(self.__width - 10, buttons_start)

        self.shape = DrawableRectangle(
            Point(0, 0),
            self.__width,
            # rows_shape.height + self.rows_cols_distance + text_shape.height
            buttons_start + self.__back_button.shape.height)
        # Workaround for TextExpandable needing an exact container_size
        super().layout(context)
Пример #18
0
 def layout_shape(self) -> DrawableRectangle:
     base_shape = self.father.shape
     return DrawableRectangle(Point(0, 0),
                              base_shape.width - self.__left - self.right,
                              base_shape.height - self.__top - self.bottom)
Пример #19
0
class Text(Widget):
    def __init__(self,
                 label: str = "",
                 width: int = 50,
                 font_size: int = 20,
                 padding: int = 5):
        super().__init__()
        self.label = label
        self._width = None
        self.width = width
        self.font_size = font_size
        self.padding = padding
        self.shape = None

        def default_acceptable(c: str) -> bool:
            if c in string.digits or c in string.punctuation \
                    or c in string.ascii_letters or c == ' ':
                return True
            else:
                return False

        self.acceptable = default_acceptable

    @property
    def width(self) -> int:
        return self._width

    @width.setter
    def width(self, value: int):
        self._width = value
        self.shape = None

    def layout(self, context: cairo.Context):
        if not self.is_shape_set:
            context.save()
            context.set_font_size(self.font_size)
            generator = (context.text_extents(chr(letter) * 10)
                         for letter in range(ord('A'),
                                             ord('Z') + 1))
            context.restore()
            sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
            max_height = 0
            for w, h in sizes:
                max_height = max(h, max_height)

            width = self.width
            height = self.padding * 3 + max_height

            self.shape = DrawableRectangle(Point(0, 0), width, height)
            # self.clip_rectangle = self.shape

    def on_draw(self, widget: Widget, context: cairo.Context):
        if not self.is_shape_set:
            self.layout(context)

        context.set_font_size(self.font_size)

        self.shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        shape = self.shape

        label = self.label
        if len(label) > 0 and label[-1] == ' ':
            label += '.'
        xb, yb, w, h, xa, ya = context.text_extents(label)
        context.rectangle(shape.start.x + self.padding, shape.start.y,
                          shape.width - self.padding, shape.height)
        context.clip()
        context.move_to(shape.start.x + (shape.width - self.padding - w) / 2,
                        shape.start.y + shape.height - self.padding)
        context.show_text(self.label)

    def on_key_down(self, widget: "Widget", event: KeyboardEvent):
        if self.is_focused:
            c = event.key
            if c == 'space':
                c = ' '
            if len(c) == 1 and self.acceptable(c):
                if 'shift' in event.modifiers:
                    c = c.upper()
                self.label += c
                self.invalidate()
            elif c == 'backspace':
                self.label = self.label[:-1]
                self.invalidate()
Пример #20
0
class Number(Widget):

    def __init__(self, digits=2, font_size=20, padding=5):
        super().__init__()
        self.digits = digits
        self.font_size = font_size
        self.padding = padding
        self._max_value = None
        self._min_value = 1
        self._value = 1
        self.shape = None

    @property
    def max_value(self) -> int:
        if self._max_value is None:
            return 10 ** self.digits - 1
        else:
            return max(self._max_value, self._min_value)

    @max_value.setter
    def max_value(self, value: int):
        self._max_value = value
        self._value = min(value, self._value)

    @property
    def min_value(self) -> int:
        return self._min_value

    @min_value.setter
    def min_value(self, value):
        self._min_value = value
        self._value = max(value, self._value)

    @property
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, value: int):
        self._value = min(self.max_value, max(self.min_value, value))

    def layout(self, context: cairo.Context):
        context.save()
        context.set_font_size(self.font_size)
        generator = (context.text_extents(str(digit) * self.digits)
                     for digit in range(10))
        sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
        max_width = 0
        max_height = 0
        for w, h in sizes:
            max_width = max(w, max_width)
            max_height = max(h, max_height)

        width = self.padding * 2 + max_width
        height = self.padding * 2 + max_height

        self.shape = DrawableRectangle(Point(0, 0), width, height)
        context.restore()

    def on_draw(self, widget: Widget, context: cairo.Context):
        shape = self.shape
        if shape is None:
            self.layout(context)
            shape = self.shape
        shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()

        text = str(self.value)
        context.set_font_size(self.font_size)
        xb, yb, w, h, xa, ya = context.text_extents(text)
        context.move_to(shape.start.x + shape.width - w - self.padding,
                        shape.start.y + shape.height - self.padding)
        context.show_text(text)

    def is_point_in(self, p: "Point", category=MouseEvent.UNKNOWN):
        if not self.is_shape_set:
            return False
        else:
            return self.shape.is_point_in(p)
Пример #21
0
class IncrementDecrement(UncheckedContainer):

    def __init__(self, height=40, width=None, padding=None, distance=None):
        super().__init__()

        if width is None:
            # width = (height / 15) * 7
            width = (height / 5) * 3
        if distance is None:
            distance = height / 5
        if padding is None:
            padding = height / 8

        arrow_width = width - 2 * padding
        arrow_height = (height - 2 * padding - distance) / 2
        self.up_arrow = Arrow(arrow_width,
                              arrow_height,
                              direction=Arrow.UP)
        self.down_arrow = Arrow(arrow_width,
                                arrow_height,
                                direction=Arrow.DOWN)

        self.up_arrow.translate(arrow_width / 2 + padding,
                                padding + arrow_height / 2)
        self.down_arrow.translate(arrow_width / 2 + padding,
                                  height - padding - arrow_height/2)

        self.add(self.up_arrow)
        self.add(self.down_arrow)
        self.shape = DrawableRectangle(Point(0, 0), width, height)

        self.increment_action = lambda: None
        self.decrement_action = lambda: None

        self.__moving_action = None
        self.__skip = 0

    def set_increment_action(self, action: Callable[[], None]):
        self.increment_action = action

    def set_decrement_action(self, action: Callable[[], None]):
        self.decrement_action = action

    def on_draw(self, widget: Widget, context: cairo.Context):
        context.save()
        self.shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        context.restore()
        super().on_draw(widget, context)

    def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN):
        return self.shape.is_point_in(p)

    def on_mouse_down(self, widget: "Widget", event: MouseEvent):
        # self.__skip = 4
        if event.y <= self.shape.height / 2:
            action = self.increment_action
        else:
            action = self.decrement_action
        self.__moving_action = RepeatedAction(action,
                                              skip=10,
                                              interval=50,
                                              first=True).start()
        return True

    def on_mouse_up(self, widget: "Widget", event: MouseEvent):
        if self.__moving_action is not None:
            self.__moving_action.end = True
        return False
Пример #22
0
class IncrementDecrement(UncheckedContainer):
    def __init__(self, height=40, width=None, padding=None, distance=None):
        super().__init__()

        if width is None:
            # width = (height / 15) * 7
            width = (height / 5) * 3
        if distance is None:
            distance = height / 5
        if padding is None:
            padding = height / 8

        arrow_width = width - 2 * padding
        arrow_height = (height - 2 * padding - distance) / 2
        self.up_arrow = Arrow(arrow_width, arrow_height, direction=Arrow.UP)
        self.down_arrow = Arrow(arrow_width,
                                arrow_height,
                                direction=Arrow.DOWN)

        self.up_arrow.translate(arrow_width / 2 + padding,
                                padding + arrow_height / 2)
        self.down_arrow.translate(arrow_width / 2 + padding,
                                  height - padding - arrow_height / 2)

        self.add(self.up_arrow)
        self.add(self.down_arrow)
        self.shape = DrawableRectangle(Point(0, 0), width, height)

        self.increment_action = lambda: None
        self.decrement_action = lambda: None

        self.__moving_action = None
        self.__skip = 0

    def set_increment_action(self, action: Callable[[], None]):
        self.increment_action = action

    def set_decrement_action(self, action: Callable[[], None]):
        self.decrement_action = action

    def on_draw(self, widget: Widget, context: cairo.Context):
        context.save()
        self.shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        context.restore()
        super().on_draw(widget, context)

    def is_point_in(self, p: Point, category=MouseEvent.UNKNOWN):
        return self.shape.is_point_in(p)

    def on_mouse_down(self, widget: "Widget", event: MouseEvent):
        # self.__skip = 4
        if event.y <= self.shape.height / 2:
            action = self.increment_action
        else:
            action = self.decrement_action
        self.__moving_action = RepeatedAction(action,
                                              skip=10,
                                              interval=50,
                                              first=True).start()
        return True

    def on_mouse_up(self, widget: "Widget", event: MouseEvent):
        if self.__moving_action is not None:
            self.__moving_action.end = True
        return False
Пример #23
0
class Text(Widget):

    def __init__(self,
                 label: str="",
                 width: int=50,
                 font_size: int=20,
                 padding: int=5):
        super().__init__()
        self.label = label
        self._width = None
        self.width = width
        self.font_size = font_size
        self.padding = padding
        self.shape = None
        def default_acceptable(c: str) -> bool:
            if c in string.digits or c in string.punctuation \
                    or c in string.ascii_letters or c == ' ':
                return True
            else:
                return False
        self.acceptable = default_acceptable

    @property
    def width(self) -> int:
        return self._width

    @width.setter
    def width(self, value: int):
        self._width = value
        self.shape = None

    def layout(self, context: cairo.Context):
        if not self.is_shape_set:
            context.save()
            context.set_font_size(self.font_size)
            generator = (context.text_extents(chr(letter) * 10)
                         for letter in range(ord('A'), ord('Z') + 1))
            context.restore()
            sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
            max_height = 0
            for w, h in sizes:
                max_height = max(h, max_height)

            width = self.width
            height = self.padding * 3 + max_height

            self.shape = DrawableRectangle(Point(0, 0), width, height)
            # self.clip_rectangle = self.shape

    def on_draw(self, widget: Widget, context: cairo.Context):
        if not self.is_shape_set:
            self.layout(context)

        context.set_font_size(self.font_size)

        self.shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        shape = self.shape

        label = self.label
        if len(label) > 0 and label[-1] == ' ':
            label += '.'
        xb, yb, w, h, xa, ya = context.text_extents(label)
        context.rectangle(shape.start.x + self.padding,
                          shape.start.y,
                          shape.width - self.padding,
                          shape.height)
        context.clip()
        context.move_to(shape.start.x + (shape.width - self.padding - w)/2,
                        shape.start.y + shape.height - self.padding)
        context.show_text(self.label)

    def on_key_down(self, widget: "Widget", event: KeyboardEvent):
        if self.is_focused:
            c = event.key
            if c == 'space':
                c = ' '
            if len(c) == 1 and self.acceptable(c):
                if 'shift' in event.modifiers:
                    c = c.upper()
                self.label += c
                self.invalidate()
            elif c == 'backspace':
                self.label = self.label[:-1]
                self.invalidate()
Пример #24
0
class Number(Widget):
    def __init__(self, digits=2, font_size=20, padding=5):
        super().__init__()
        self.digits = digits
        self.font_size = font_size
        self.padding = padding
        self._max_value = None
        self._min_value = 1
        self._value = 1
        self.shape = None

    @property
    def max_value(self) -> int:
        if self._max_value is None:
            return 10**self.digits - 1
        else:
            return max(self._max_value, self._min_value)

    @max_value.setter
    def max_value(self, value: int):
        self._max_value = value
        self._value = min(value, self._value)

    @property
    def min_value(self) -> int:
        return self._min_value

    @min_value.setter
    def min_value(self, value):
        self._min_value = value
        self._value = max(value, self._value)

    @property
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, value: int):
        self._value = min(self.max_value, max(self.min_value, value))

    def layout(self, context: cairo.Context):
        context.save()
        context.set_font_size(self.font_size)
        generator = (context.text_extents(str(digit) * self.digits)
                     for digit in range(10))
        sizes = [(xa, h) for xb, yb, w, h, xa, ya in generator]
        max_width = 0
        max_height = 0
        for w, h in sizes:
            max_width = max(w, max_width)
            max_height = max(h, max_height)

        width = self.padding * 2 + max_width
        height = self.padding * 2 + max_height

        self.shape = DrawableRectangle(Point(0, 0), width, height)
        context.restore()

    def on_draw(self, widget: Widget, context: cairo.Context):
        shape = self.shape
        if shape is None:
            self.layout(context)
            shape = self.shape
        shape.draw_on_context(context)
        context.set_source_rgb(1, 1, 1)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()

        text = str(self.value)
        context.set_font_size(self.font_size)
        xb, yb, w, h, xa, ya = context.text_extents(text)
        context.move_to(shape.start.x + shape.width - w - self.padding,
                        shape.start.y + shape.height - self.padding)
        context.show_text(text)

    def is_point_in(self, p: "Point", category=MouseEvent.UNKNOWN):
        if not self.is_shape_set:
            return False
        else:
            return self.shape.is_point_in(p)
Пример #25
0
class BetterSelector(UncheckedContainer):

    def __init__(self, size=20):

        super().__init__()

        self.option_colours = [
            global_constants.start_selected,
            global_constants.start_empty,
            global_constants.start_default
        ]

        self._buttons = [
            BetterButton("",
                         padding=size/2,
                         background_color=colour,
                         origin=BetterButton.LEFT)
            for colour in self.option_colours
        ]

        for b in self._buttons:
            self.add(b)

        self.bounding_rectangle = None

        self.size = size
        self.padding = 5

        self._handle_layout()

    def set_click_action(self, index: int, action: Callable[[MouseButton], None]):
        self._buttons[index].on_click_action = action

    def _handle_layout(self):
        start_y = self.padding
        for button in self._buttons:
            button.set_translate(self.padding, start_y)
            start_y += self.size + self.padding

        self.bounding_rectangle = DrawableRectangle(
            Point(0, 0),
            self.padding * 2 + self.size,
            start_y
        )

        self.shape = self.bounding_rectangle

    @property
    def width(self) -> int:
        return self.bounding_rectangle.width + 1

    def on_draw(self, widget: Widget, context: cairo.Context):
        context.save()
        context.set_line_width(1)
        self.bounding_rectangle.draw_on_context(context)
        context.set_source_rgb(*global_constants.background)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        context.restore()
        super().on_draw(widget, context)
Пример #26
0
class ButtonedTextOverlay(UncheckedContainer):

    def __init__(self,
                 label: str,
                 font_size: int=20,
                 back_label: str="Back",
                 ok_label: str="Ok",
                 background_color: Tuple[Number, Number, Number]=(.3, .3, .3),
                 background_alpha: Number=.5):
        super().__init__()

        self.label = label
        self.font_size = font_size
        self.background_color = background_color
        self.background_alpha = background_alpha
        self.padding = 20

        self.__background_rectangle = None
        self.__overlay_shape = None
        self.__label_start = Point(0, 0)

        button_font_size = font_size * .8
        self.__back_button = BetterButton(back_label, button_font_size, origin=BetterButton.LEFT)
        self.__ok_button = BetterButton(ok_label, button_font_size, origin=BetterButton.RIGHT)

        self.add(self.__back_button)
        self.add(self.__ok_button)

    def set_back_action(self, action: Callable[[], None]):
        self.__back_button.on_click_action = click_left_button_wrapper(action)

    def set_ok_action(self, action: Callable[[], None]):
        self.__ok_button.on_click_action = click_left_button_wrapper(action)

    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
            )


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

        context.set_source_rgba(*self.background_color, self.background_alpha)
        self.__background_rectangle.draw_on_context(context)
        context.fill()

        context.set_font_size(self.font_size)

        self.__overlay_shape.draw_on_context(context)
        context.set_source_rgb(.3, .3, .3)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()

        context.move_to(self.__label_start.x, self.__label_start.y)
        context.show_text(self.label)
        super().on_draw(widget, context)

    def is_point_in(self, p: "Point", category=MouseEvent.UNKNOWN):
        return self.visible

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

    def on_mouse_move(self, widget: Widget, event: MouseEvent):
        super().on_mouse_move(widget, event)
        return True
Пример #27
0
class Zoom(UncheckedContainer):

    def __init__(self, font_size=15, padding=10):

        super().__init__()

        self.padding = padding

        self.__plus_button = ZoomButton("+", font_size, origin=BetterButton.LEFT)
        self.__minus_button = ZoomButton("‒", font_size, origin=BetterButton.LEFT)

        self.add(self.__plus_button)
        self.add(self.__minus_button)

    @property
    def plus_action(self) -> Callable[[], None]:
        return self.__plus_button.action

    @plus_action.setter
    def plus_action(self, action: Callable[[], None]):
        self.__plus_button.action = action

    @property
    def minus_action(self) -> Callable[[], None]:
        return self.__minus_button.action

    @minus_action.setter
    def minus_action(self, action: Callable[[], None]):
        self.__minus_button.action = action

    def layout(self, context: cairo.Context):
        super().layout(context)
        context.save()
        self.__plus_button.layout(context)
        context.restore()
        self.__minus_button.min_height = self.__plus_button.shape.height
        context.save()
        self.__minus_button.layout(context)
        context.restore()

        plus_shape = self.__plus_button.shape
        minus_shape = self.__minus_button.shape

        self.__minus_button.set_translate(self.padding, self.padding/2)
        self.__plus_button.set_translate(
            self.padding + minus_shape.width + self.padding,
            self.padding/2
        )

        self.shape = DrawableRectangle(
            Point(0, 0),
            plus_shape.width + minus_shape.width + 3 * self.padding,
            self.padding + max(plus_shape.height, minus_shape.height)
        )

    def on_draw(self, widget: Widget, context: cairo.Context):
        self.shape.draw_on_context(context)
        context.set_source_rgb(*global_constants.background)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()
        super().on_draw(widget, context)
Пример #28
0
class TextOverlay(Widget):

    def __init__(self,
                 label: str,
                 font_size: int=20,
                 background_color: Tuple[Number, Number, Number]=(.3, .3, .3),
                 background_alpha: Number=.5):
        super().__init__()
        self.__background_rectangle = None
        self.__label_rectangle = None
        self.padding = 20
        self.background_color = background_color
        self.background_alpha = background_alpha
        self.label = label
        self.font_size = font_size
        self.on_click_action = None

    def is_point_in(self,p: Point, category=MouseEvent.UNKNOWN):
        return True

    def on_click(self, widget: "Widget", button: MouseButton):
        if self.on_click_action is not None:
            self.on_click_action()

    def layout(self, context: cairo.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

        rectangle_width = xa + 2 * padding
        rectangle_height = padding + h
        start_x = (width - rectangle_width) / 2
        start_y = (height - rectangle_height) / 2

        self.__label_rectangle = DrawableRectangle(Point(start_x, start_y),
                                                   rectangle_width,
                                                   rectangle_height)

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

        context.set_source_rgba(*self.background_color, self.background_alpha)
        self.__background_rectangle.draw_on_context(context)
        context.fill()

        context.set_font_size(self.font_size)

        self.__label_rectangle.draw_on_context(context)
        context.set_source_rgb(.3, .3, .3)
        context.fill_preserve()
        context.set_source_rgb(0, 0, 0)
        context.stroke()

        rectangle = self.__label_rectangle
        start = rectangle.start
        context.move_to(start.x + self.padding,
                        start.y + rectangle.height - self.padding/2)
        context.show_text(self.label)

    def on_mouse_down(self, widget: "Widget", event: MouseEvent) -> bool:
        super().on_mouse_down(widget, event)
        return True

    # def on_mouse_up(self, widget: "Widget", event: MouseEvent) -> bool:
    #     super().on_mouse_up(widget, event)
    #     return True

    def on_mouse_move(self, widget: "Widget", event: MouseEvent) -> bool:
        super().on_mouse_move(widget, event)
        return True