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 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)
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
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 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)
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 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 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)
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 )
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)
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)
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()
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 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 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)
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)
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()
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)
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
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
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()
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)
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)
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
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)
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