Ejemplo n.º 1
0
class ImagePanel(Widget):
    def __init__(self, parent, *, is_tilemap_mode):
        y, height = (80, 66) if is_tilemap_mode else (16, 130)
        super().__init__(parent, 157, y, 66, height)

        self._is_tilemap_mode = is_tilemap_mode
        self.viewport_x = 0
        self.viewport_y = 0
        self._focus_x = 0
        self._focus_y = 0
        self._focus_width = 8 if is_tilemap_mode else 16
        self._focus_height = 8 if is_tilemap_mode else 16
        self._press_x = 0
        self._press_y = 0
        self._drag_offset_x = 0
        self._drag_offset_y = 0
        self._tile_table = np.arange(1024).reshape(32, 32)
        self._h_scroll_bar = ScrollBar(self, 157, 145, 66,
                                       ScrollBar.HORIZONTAL, 32, 8, 0)
        self._v_scroll_bar = (ScrollBar(
            self, 222, 80, 66, ScrollBar.VERTICAL,
            32, 8, 0) if is_tilemap_mode else ScrollBar(
                self, 222, 16, 130, ScrollBar.VERTICAL, 32, 16, 0))

        self.add_event_handler("mouse_down", self.__on_mouse_down)
        self.add_event_handler("mouse_drag", self.__on_mouse_drag)
        self.add_event_handler("mouse_hover", self.__on_mouse_hover)
        self.add_event_handler("update", self.__on_update)
        self.add_event_handler("draw", self.__on_draw)
        self._h_scroll_bar.add_event_handler("change",
                                             self.__on_h_scroll_bar_change)
        self._v_scroll_bar.add_event_handler("change",
                                             self.__on_v_scroll_bar_change)

    @property
    def focused_tiles(self):
        x = self._focus_x // 8
        y = self._focus_y // 8
        width = self._focus_width // 8
        height = self._focus_height // 8

        return self._tile_table[y:y + height, x:x + width]

    def set_focus(self, x, y):
        self._focus_x = min(max(x, 0), 256 - self._focus_width)
        self._focus_y = min(max(y, 0), 256 - self._focus_height)

        offset_left = self.viewport_x - self._focus_x
        if offset_left > 0:
            self.viewport_x -= offset_left

        offset_right = self._focus_x + self._focus_width - self.viewport_x - 64
        if offset_right > 0:
            self.viewport_x += offset_right

        offset_top = self.viewport_y - self._focus_y
        if offset_top > 0:
            self.viewport_y -= offset_top

        offset_bottom = self._focus_y + self._focus_height - self.viewport_y - 64
        if offset_bottom > 0:
            self.viewport_y += offset_bottom

    def _screen_to_view(self, x, y):
        x = (x + self.viewport_x - self.x - 1) // 8 * 8
        y = (y + self.viewport_y - self.y - 1) // 8 * 8
        return x, y

    def __on_mouse_down(self, key, x, y):
        if key == pyxel.MOUSE_LEFT_BUTTON:
            x, y = self._screen_to_view(x, y)

            if self._is_tilemap_mode:
                x = min(max(x, 0), 248)
                y = min(max(y, 0), 248)

                self._press_x = x
                self._press_y = y

                self.set_focus(x, y)
            else:
                self._focus_x = min(max(x, 0), 240)
                self._focus_y = min(max(y, 0), 240)

                self.parent.drawing_x = self._focus_x
                self.parent.drawing_y = self._focus_y

        if key == pyxel.MOUSE_RIGHT_BUTTON:
            self._drag_offset_x = 0
            self._drag_offset_y = 0

    def __on_mouse_drag(self, key, x, y, dx, dy):
        if key == pyxel.MOUSE_LEFT_BUTTON:
            if self._is_tilemap_mode:
                x, y = self._screen_to_view(x, y)

                x = min(max(x, 0), 248)
                y = min(max(y, 0), 248)

                self._focus_x = min(self._press_x, x)
                self._focus_y = min(self._press_y, y)

                self._focus_width = min(abs(self._press_x - x) + 8, 64)
                self._focus_height = min(abs(self._press_y - y) + 8, 64)
            else:
                self.__on_mouse_down(key, x, y)
        elif key == pyxel.MOUSE_RIGHT_BUTTON:
            self._drag_offset_x -= dx
            self._drag_offset_y -= dy

            if abs(self._drag_offset_x) >= 8:
                offset = (self._drag_offset_x // 8) * 8
                self._drag_offset_x -= offset
                self.viewport_x += offset

            if abs(self._drag_offset_y) >= 8:
                offset = (self._drag_offset_y // 8) * 8
                self._drag_offset_y -= offset
                self.viewport_y += offset

            self.viewport_x = min(max(self.viewport_x, 0), 192)
            self.viewport_y = min(max(self.viewport_y, 0),
                                  192 if self._is_tilemap_mode else 128)

    def __on_mouse_hover(self, x, y):
        x, y = self._screen_to_view(x, y)
        s = "VIEW:R-DRAG" if self._is_tilemap_mode else "TARGET:CURSOR IMPORT:DROP"
        self.parent.help_message = s + " ({},{})".format(x, y)

    def __on_update(self):
        if not self._is_tilemap_mode:
            self._focus_x = self.parent.drawing_x
            self._focus_y = self.parent.drawing_y

        self._h_scroll_bar.value = self.viewport_x // 8
        self._v_scroll_bar.value = self.viewport_y // 8

    def __on_draw(self):
        self.draw_panel(self.x, self.y, self.width, self.height)

        pyxel.blt(
            self.x + 1,
            self.y + 1,
            self.parent.image,
            self.viewport_x,
            self.viewport_y,
            self.width - 2,
            self.height - 2,
        )

        pyxel.clip(self.x + 1, self.y + 1, self.x + self.width - 2,
                   self.y + self.height - 2)

        x1 = self.x + self._focus_x - self.viewport_x + 1
        y1 = self.y + self._focus_y - self.viewport_y + 1
        x2 = x1 + self._focus_width - 1
        y2 = y1 + self._focus_height - 1

        pyxel.rectb(x1, y1, x2, y2, 7)
        pyxel.rectb(x1 + 1, y1 + 1, x2 - 1, y2 - 1, 0)
        pyxel.rectb(x1 - 1, y1 - 1, x2 + 1, y2 + 1, 0)

        pyxel.clip()

    def __on_h_scroll_bar_change(self, value):
        self.viewport_x = value * 8

    def __on_v_scroll_bar_change(self, value):
        self.viewport_y = value * 8
Ejemplo n.º 2
0
class ImageFrame(Widget):
    def __init__(self, parent, *, is_tilemap_mode):
        y, height = (80, 66) if is_tilemap_mode else (16, 130)
        super().__init__(parent, 157, y, 66, height)

        self._is_tilemap_mode = is_tilemap_mode
        self.viewport_x = 0
        self.viewport_y = 0
        self.select_x = 0
        self.select_y = 0
        self._drag_offset_x = 0
        self._drag_offset_y = 0
        self._h_scroll_bar = ScrollBar(
            self, 157, 145, 66, ScrollBar.HORIZONTAL, 32, 8, 0
        )
        self._v_scroll_bar = (
            ScrollBar(self, 222, 80, 66, ScrollBar.VERTICAL, 32, 8, 0)
            if is_tilemap_mode
            else ScrollBar(self, 222, 16, 130, ScrollBar.VERTICAL, 32, 16, 0)
        )

        self.add_event_handler("mouse_down", self.__on_mouse_down)
        self.add_event_handler("mouse_drag", self.__on_mouse_drag)
        self.add_event_handler("mouse_hover", self.__on_mouse_hover)
        self.add_event_handler("update", self.__on_update)
        self.add_event_handler("draw", self.__on_draw)
        self._h_scroll_bar.add_event_handler("change", self.__on_h_scroll_bar_change)
        self._v_scroll_bar.add_event_handler("change", self.__on_v_scroll_bar_change)

    def _screen_to_view(self, x, y):
        x -= self.x + 1
        y -= self.y + 1

        if self._is_tilemap_mode:
            x = self.viewport_x + min(max(x // 8, 0), 7) * 8
            y = self.viewport_y + min(max(y // 8, 0), 7) * 8
        else:
            x = self.viewport_x + min(max((x - 4) // 8, 0), 6) * 8
            y = self.viewport_y + min(max((y - 4) // 8, 0), 14) * 8

        return x, y

    def __on_mouse_down(self, key, x, y):
        if key == pyxel.KEY_LEFT_BUTTON:
            self.select_x, self.select_y = self._screen_to_view(x, y)

            if not self._is_tilemap_mode:
                self.parent.edit_x = self.select_x
                self.parent.edit_y = self.select_y

        if key == pyxel.KEY_RIGHT_BUTTON:
            self._drag_offset_x = 0
            self._drag_offset_y = 0

    def __on_mouse_drag(self, key, x, y, dx, dy):
        if key == pyxel.KEY_LEFT_BUTTON:
            self.__on_mouse_down(key, x, y)
        elif key == pyxel.KEY_RIGHT_BUTTON:
            self._drag_offset_x -= dx
            self._drag_offset_y -= dy

            if abs(self._drag_offset_x) >= 8:
                offset = (self._drag_offset_x // 8) * 8
                self._drag_offset_x -= offset
                self.viewport_x += offset

            if abs(self._drag_offset_y) >= 8:
                offset = (self._drag_offset_y // 8) * 8
                self._drag_offset_y -= offset
                self.viewport_y += offset

            self.viewport_x = min(max(self.viewport_x, 0), 192)
            self.viewport_y = min(
                max(self.viewport_y, 0), 192 if self._is_tilemap_mode else 128
            )

    def __on_mouse_hover(self, x, y):
        x, y = self._screen_to_view(x, y)
        s = "VIEW:R-DRAG" if self._is_tilemap_mode else "TARGET:CURSOR VIEW:R-DRAG"
        self.parent.help_message = s + " ({},{})".format(x, y)

    def __on_update(self):
        if not self._is_tilemap_mode:
            self.select_x = self.parent.edit_x
            self.select_y = self.parent.edit_y

        self._h_scroll_bar.value = self.viewport_x // 8
        self._v_scroll_bar.value = self.viewport_y // 8

    def __on_draw(self):
        self.draw_frame(self.x, self.y, self.width, self.height)

        pyxel.blt(
            self.x + 1,
            self.y + 1,
            self.parent.image,
            self.viewport_x,
            self.viewport_y,
            self.width - 2,
            self.height - 2,
        )

        pyxel.clip(
            self.x + 1, self.y + 1, self.x + self.width - 2, self.y + self.height - 2
        )

        x = self.x + self.select_x - self.viewport_x
        y = self.y + self.select_y - self.viewport_y

        if self._is_tilemap_mode:
            pyxel.rectb(x, y, x + 9, y + 9, 0)
            pyxel.rectb(x - 1, y - 1, x + 10, y + 10, 7)
            pyxel.rectb(x - 2, y - 2, x + 11, y + 11, 0)
        else:
            pyxel.rectb(x, y, x + 17, y + 17, 0)
            pyxel.rectb(x - 1, y - 1, x + 18, y + 18, 7)
            pyxel.rectb(x - 2, y - 2, x + 19, y + 19, 0)

        pyxel.clip()

    def __on_h_scroll_bar_change(self, value):
        self.viewport_x = value * 8

    def __on_v_scroll_bar_change(self, value):
        self.viewport_y = value * 8
Ejemplo n.º 3
0
class ImageFrame(Widget):
    def __init__(self, parent, *, is_tilemap_mode):
        y, height = (81, 64) if is_tilemap_mode else (17, 128)
        super().__init__(parent, 158, y, 64, height)

        self._is_tilemap_mode = is_tilemap_mode

        self.viewport_x = 0
        self.viewport_y = 0

        self.select_x = 0
        self.select_y = 0

        self._drag_offset_x = 0
        self._drag_offset_y = 0

        self._h_scroll_bar = ScrollBar(self, 157, 145, 66, "horizontal", 32, 8)
        self._h_scroll_bar.add_event_handler("change", self.__on_change_x)

        if is_tilemap_mode:
            self._v_scroll_bar = ScrollBar(self, 222, 80, 66, "vertical", 32,
                                           8)
        else:
            self._v_scroll_bar = ScrollBar(self, 222, 16, 130, "vertical", 32,
                                           16)

        self._v_scroll_bar.add_event_handler("change", self.__on_change_y)

        self.add_event_handler("mouse_down", self.__on_mouse_down)
        self.add_event_handler("mouse_drag", self.__on_mouse_drag)
        self.add_event_handler("update", self.__on_update)
        self.add_event_handler("draw", self.__on_draw)

    def __on_change_x(self, value):
        self.viewport_x = value * 8

    def __on_change_y(self, value):
        self.viewport_y = value * 8

    def __on_mouse_down(self, key, x, y):
        if key == pyxel.KEY_LEFT_BUTTON:
            x -= self.x
            y -= self.y

            if self._is_tilemap_mode:
                self.select_x = self.viewport_x + min(max(x // 8, 0), 7) * 8
                self.select_y = self.viewport_y + min(max(y // 8, 0), 7) * 8
            else:
                self.select_x = self.viewport_x + min(max(
                    (x - 4) // 8, 0), 6) * 8
                self.select_y = self.viewport_y + min(max(
                    (y - 4) // 8, 0), 14) * 8

                self.parent.edit_x = self.select_x
                self.parent.edit_y = self.select_y

        if key == pyxel.KEY_RIGHT_BUTTON:
            self._drag_offset_x = 0
            self._drag_offset_y = 0

    def __on_mouse_drag(self, key, x, y, dx, dy):
        if key == pyxel.KEY_LEFT_BUTTON:
            self.__on_mouse_down(key, x, y)
        elif key == pyxel.KEY_RIGHT_BUTTON:
            self._drag_offset_x -= dx
            self._drag_offset_y -= dy

            if abs(self._drag_offset_x) >= 8:
                offset = (self._drag_offset_x // 8) * 8
                self._drag_offset_x -= offset
                self.viewport_x += offset

            if abs(self._drag_offset_y) >= 8:
                offset = (self._drag_offset_y // 8) * 8
                self._drag_offset_y -= offset
                self.viewport_y += offset

            self.viewport_x = min(max(self.viewport_x, 0), 192)
            self.viewport_y = min(max(self.viewport_y, 0),
                                  192 if self._is_tilemap_mode else 128)

    def __on_update(self):
        self._h_scroll_bar.value = self.viewport_x // 8
        self._v_scroll_bar.value = self.viewport_y // 8

    def __on_draw(self):
        pyxel.blt(
            self.x,
            self.y,
            self.parent.image,
            self.viewport_x,
            self.viewport_y,
            self.width,
            self.height,
        )

        pyxel.clip(self.x - 1, self.y - 1, self.x + self.width,
                   self.y + self.height)

        x = self.x + self.select_x - self.viewport_x
        y = self.y + self.select_y - self.viewport_y

        if self._is_tilemap_mode:
            pyxel.rectb(x - 1, y - 1, x + 8, y + 8, 7)
        else:
            pyxel.rectb(x - 1, y - 1, x + 16, y + 16, 7)

        pyxel.clip()
Ejemplo n.º 4
0
class DrawingPanel(Widget):
    def __init__(self, parent, *, is_tilemap_mode):
        super().__init__(parent, 11, 16, 130, 130)

        self._is_tilemap_mode = is_tilemap_mode
        self._history_data = None
        self.viewport_x = 0
        self.viewport_y = 0
        self._press_x = 0
        self._press_y = 0
        self._last_x = 0
        self._last_y = 0
        self._drag_offset_x = 0
        self._drag_offset_y = 0
        self._select_x1 = 0
        self._select_y1 = 0
        self._select_x2 = 0
        self._select_y2 = 0
        self._copy_buffer = None
        self._is_dragged = False
        self._is_assist_mode = False
        self._overlay_canvas = OverlayCanvas()
        self._h_scroll_bar = ScrollBar(self, 11, 145, 130,
                                       ScrollBar.HORIZONTAL, 32, 2, 0)
        self._v_scroll_bar = ScrollBar(self, 140, 16, 130, ScrollBar.VERTICAL,
                                       32, 2, 0)

        self.add_event_handler("mouse_down", self.__on_mouse_down)
        self.add_event_handler("mouse_up", self.__on_mouse_up)
        self.add_event_handler("mouse_click", self.__on_mouse_click)
        self.add_event_handler("mouse_drag", self.__on_mouse_drag)
        self.add_event_handler("mouse_hover", self.__on_mouse_hover)
        self.add_event_handler("update", self.__on_update)
        self.add_event_handler("draw", self.__on_draw)
        self._h_scroll_bar.add_event_handler("change",
                                             self.__on_h_scroll_bar_change)
        self._v_scroll_bar.add_event_handler("change",
                                             self.__on_v_scroll_bar_change)

    def _add_pre_history(self, canvas):
        self._history_data = data = {}

        if self._is_tilemap_mode:
            data["tilemap"] = self.parent.tilemap
        else:
            data["image"] = self.parent.image

        data["pos"] = (self.viewport_x, self.viewport_y)
        data["before"] = canvas.copy()

    def _add_post_history(self, canvas):
        data = self._history_data
        data["after"] = canvas.copy()

        if (data["before"] != data["after"]).any():
            self.parent.add_history(data)

    def _screen_to_view(self, x, y):
        x = min(max((x - self.x - 1) // 8, 0), 15)
        y = min(max((y - self.y - 1) // 8, 0), 15)
        return x, y

    def __on_mouse_down(self, key, x, y):
        if key != pyxel.MOUSE_LEFT_BUTTON:
            return

        x, y = self._screen_to_view(x, y)

        self._press_x = x
        self._press_y = y

        self._is_dragged = True
        self._is_assist_mode = False

        if self.parent.tool == TOOL_SELECT:
            self._select_x1 = self._select_x2 = x
            self._select_y1 = self._select_y2 = y
        elif self.parent.tool == TOOL_PENCIL:
            self._overlay_canvas.pix(x, y, self.parent.color)
        elif self.parent.tool >= TOOL_RECTB and self.parent.tool <= TOOL_CIRC:
            self._overlay_canvas.rect(x, y, x, y, self.parent.color, False)
        elif self.parent.tool == TOOL_BUCKET:
            data = (pyxel.tilemap(self.parent.tilemap).data
                    if self._is_tilemap_mode else pyxel.image(
                        self.parent.image).data)
            dest = data[self.viewport_y:self.viewport_y + 16,
                        self.viewport_x:self.viewport_x + 16, ]

            self._add_pre_history(dest)

            self._overlay_canvas.fill(x, y, self.parent.color, dest)

            self._add_post_history(dest)

        self._last_x = x
        self._last_y = y

    def __on_mouse_up(self, key, x, y):
        if key != pyxel.MOUSE_LEFT_BUTTON:
            return

        self._is_dragged = False

        if self.parent.tool >= TOOL_PENCIL and self.parent.tool <= TOOL_CIRC:
            data = (pyxel.tilemap(self.parent.tilemap).data
                    if self._is_tilemap_mode else pyxel.image(
                        self.parent.image).data)
            dest = data[self.viewport_y:self.viewport_y + 16,
                        self.viewport_x:self.viewport_x + 16, ]

            self._add_pre_history(dest)

            index = self._overlay_canvas.data != OverlayCanvas.COLOR_NONE
            dest[index] = self._overlay_canvas.data[index]
            self._overlay_canvas.clear()

            self._add_post_history(dest)

    def __on_mouse_click(self, key, x, y):
        if key == pyxel.MOUSE_RIGHT_BUTTON:
            x = self.viewport_x + (x - self.x) // 8
            y = self.viewport_y + (y - self.y) // 8

            if self._is_tilemap_mode:
                self.parent.color = pyxel.tilemap(self.parent.tilemap).data[y,
                                                                            x]
            else:
                self.parent.color = pyxel.image(self.parent.image).data[y, x]

    def __on_mouse_drag(self, key, x, y, dx, dy):
        if key == pyxel.MOUSE_LEFT_BUTTON:
            x1 = self._press_x
            y1 = self._press_y
            x2 = (x - self.x - 1) // 8
            y2 = (y - self.y - 1) // 8

            if self.parent.tool == TOOL_SELECT:
                x2 = min(max(x2, 0), 15)
                y2 = min(max(y2, 0), 15)
                self._select_x1, self._select_x2 = (x1,
                                                    x2) if x1 < x2 else (x2,
                                                                         x1)
                self._select_y1, self._select_y2 = (y1,
                                                    y2) if y1 < y2 else (y2,
                                                                         y1)
            elif self.parent.tool == TOOL_PENCIL:
                if self._is_assist_mode:
                    self._overlay_canvas.clear()
                    self._overlay_canvas.line(x1, y1, x2, y2,
                                              self.parent.color)
                else:
                    self._overlay_canvas.line(self._last_x, self._last_y, x2,
                                              y2, self.parent.color)
            elif self.parent.tool == TOOL_RECTB:
                self._overlay_canvas.clear()
                self._overlay_canvas.rectb(x1, y1, x2, y2, self.parent.color,
                                           self._is_assist_mode)
            elif self.parent.tool == TOOL_RECT:
                self._overlay_canvas.clear()
                self._overlay_canvas.rect(x1, y1, x2, y2, self.parent.color,
                                          self._is_assist_mode)
            elif self.parent.tool == TOOL_CIRCB:
                self._overlay_canvas.clear()
                self._overlay_canvas.circb(x1, y1, x2, y2, self.parent.color,
                                           self._is_assist_mode)
            elif self.parent.tool == TOOL_CIRC:
                self._overlay_canvas.clear()
                self._overlay_canvas.circ(x1, y1, x2, y2, self.parent.color,
                                          self._is_assist_mode)

            self._last_x = x2
            self._last_y = y2

        elif key == pyxel.MOUSE_RIGHT_BUTTON:
            self._drag_offset_x -= dx
            self._drag_offset_y -= dy

            if abs(self._drag_offset_x) >= 16:
                offset = self._drag_offset_x // 16
                self.viewport_x += offset * 8
                self._drag_offset_x -= offset * 16

            if abs(self._drag_offset_y) >= 16:
                offset = self._drag_offset_y // 16
                self.viewport_y += offset * 8
                self._drag_offset_y -= offset * 16

            self.viewport_x = min(max(self.viewport_x, 0), 240)
            self.viewport_y = min(max(self.viewport_y, 0), 240)

    def __on_mouse_hover(self, x, y):
        if self.parent.tool == TOOL_SELECT:
            s = "COPY:CTRL+C PASTE:CTRL+V"
        elif self._is_dragged:
            s = "ASSIST:SHIFT"
        else:
            s = "PICK:R-CLICK VIEW:R-DRAG"

        x, y = self._screen_to_view(x, y)
        x += self.viewport_x
        y += self.viewport_y
        self.parent.help_message = s + " ({},{})".format(x, y)

    def __on_update(self):
        if self._is_dragged and not self._is_assist_mode and pyxel.btn(
                pyxel.KEY_SHIFT):
            self._is_assist_mode = True

            x1 = self._press_x
            y1 = self._press_y
            x2 = self._last_x
            y2 = self._last_y

            if self.parent.tool == TOOL_PENCIL:
                self._overlay_canvas.clear()
                self._overlay_canvas.line(x1, y1, x2, y2, self.parent.color)
            elif self.parent.tool == TOOL_RECTB:
                self._overlay_canvas.clear()
                self._overlay_canvas.rectb(x1, y1, x2, y2, self.parent.color,
                                           True)
            elif self.parent.tool == TOOL_RECT:
                self._overlay_canvas.clear()
                self._overlay_canvas.rect(x1, y1, x2, y2, self.parent.color,
                                          True)
            elif self.parent.tool == TOOL_CIRCB:
                self._overlay_canvas.clear()
                self._overlay_canvas.circb(x1, y1, x2, y2, self.parent.color,
                                           True)
            elif self.parent.tool == TOOL_CIRC:
                self._overlay_canvas.clear()
                self._overlay_canvas.circ(x1, y1, x2, y2, self.parent.color,
                                          True)

        if (self.parent.tool == TOOL_SELECT and self._select_x1 >= 0
                and pyxel.btn(pyxel.KEY_CONTROL)):
            if pyxel.btnp(pyxel.KEY_C):
                if self._is_tilemap_mode:
                    data = pyxel.tilemap(self.parent.tilemap).data
                else:
                    data = pyxel.image(self.parent.image).data

                src = data[self.viewport_y + self._select_y1:self.viewport_y +
                           self._select_y2 + 1,
                           self.viewport_x + self._select_x1:self.viewport_x +
                           self._select_x2 + 1, ]
                self._copy_buffer = src.copy()
            elif self._copy_buffer is not None and pyxel.btnp(pyxel.KEY_V):
                x1 = self.viewport_x + self._select_x1
                y1 = self.viewport_y + self._select_y1

                height, width = self._copy_buffer.shape
                width -= max(self._select_x1 + width - 16, 0)
                height -= max(self._select_y1 + height - 16, 0)

                if self._is_tilemap_mode:
                    data = pyxel.tilemap(self.parent.tilemap).data
                else:
                    data = pyxel.image(self.parent.image).data

                dest = data[y1:y1 + height, x1:x1 + width]
                dest[:, :] = self._copy_buffer[:height, :width]

        if (pyxel.btn(pyxel.KEY_SHIFT) or pyxel.btn(pyxel.KEY_CONTROL)
                or pyxel.btn(pyxel.KEY_ALT)):
            return

        if pyxel.btnp(pyxel.KEY_LEFT, WIDGET_HOLD_TIME, WIDGET_REPEAT_TIME):
            self.viewport_x -= 8

        if pyxel.btnp(pyxel.KEY_RIGHT, WIDGET_HOLD_TIME, WIDGET_REPEAT_TIME):
            self.viewport_x += 8

        if pyxel.btnp(pyxel.KEY_UP, WIDGET_HOLD_TIME, WIDGET_REPEAT_TIME):
            self.viewport_y -= 8

        if pyxel.btnp(pyxel.KEY_DOWN, WIDGET_HOLD_TIME, WIDGET_REPEAT_TIME):
            self.viewport_y += 8

        self.viewport_x = min(max(self.viewport_x, 0), 240)
        self.viewport_y = min(max(self.viewport_y, 0), 240)

        self._h_scroll_bar.value = self.viewport_x // 8
        self._v_scroll_bar.value = self.viewport_y // 8

    def __on_draw(self):
        self.draw_panel(self.x, self.y, self.width, self.height)

        if self._is_tilemap_mode:
            pyxel.bltm(
                self.x + 1,
                self.y + 1,
                self.parent.image,
                self.parent.tilemap,
                self.viewport_x,
                self.viewport_y,
                16,
                16,
            )

            for i in range(16):
                y = self.y + i * 8 + 1
                for j in range(16):
                    x = self.x + j * 8 + 1

                    val = self._overlay_canvas.data[i, j]
                    if val != OverlayCanvas.COLOR_NONE:
                        sx = (val % 32) * 8
                        sy = (val // 32) * 8
                        pyxel.blt(x, y, self.parent.image, sx, sy, 8, 8)
        else:
            for i in range(16):
                y = self.y + i * 8 + 1
                for j in range(16):
                    x = self.x + j * 8 + 1

                    val = self._overlay_canvas.data[i, j]
                    if val != OverlayCanvas.COLOR_NONE:
                        col = self._overlay_canvas.data[i, j]
                    else:
                        data = pyxel.image(self.parent.image).data
                        col = data[self.viewport_y + i, self.viewport_x + j]

                    pyxel.rect(x, y, x + 7, y + 7, col)

        pyxel.line(self.x + 1, self.y + 64, self.x + 128, self.y + 64, 1)
        pyxel.line(self.x + 64, self.y + 1, self.x + 64, self.y + 128, 1)

        if self.parent.tool == TOOL_SELECT and self._select_x1 >= 0:
            pyxel.clip(self.x + 1, self.y + 1, self.x + 128, self.y + 128)

            x1 = self._select_x1 * 8 + 12
            y1 = self._select_y1 * 8 + 17
            x2 = self._select_x2 * 8 + 19
            y2 = self._select_y2 * 8 + 24

            pyxel.rectb(x1, y1, x2, y2, 15)
            pyxel.rectb(x1 + 1, y1 + 1, x2 - 1, y2 - 1, 0)
            pyxel.rectb(x1 - 1, y1 - 1, x2 + 1, y2 + 1, 0)

            pyxel.clip()

    def __on_h_scroll_bar_change(self, value):
        self.viewport_x = value * 8

    def __on_v_scroll_bar_change(self, value):
        self.viewport_y = value * 8
Ejemplo n.º 5
0
class EditFrame(Widget):
    def __init__(self, parent, *, is_tilemap_mode):
        super().__init__(parent, 12, 17, 128, 128)

        self._is_tilemap_mode = is_tilemap_mode

        self.viewport_x = 0
        self.viewport_y = 0

        self._press_x = 0
        self._press_y = 0

        self._last_x = 0
        self._last_y = 0

        self._drag_offset_x = 0
        self._drag_offset_y = 0

        self._select_x1 = 0
        self._select_y1 = 0
        self._select_x2 = 0
        self._select_y2 = 0
        self._copy_buffer = None

        self._is_dragged = False
        self._is_guide_mode = False

        self._h_scroll_bar = ScrollBar(self, 11, 145, 130, "horizontal", 32, 2)
        self._h_scroll_bar.add_event_handler("change", self.__on_change_x)

        self._v_scroll_bar = ScrollBar(self, 140, 16, 130, "vertical", 32, 2)
        self._v_scroll_bar.add_event_handler("change", self.__on_change_y)

        self.add_event_handler("mouse_down", self.__on_mouse_down)
        self.add_event_handler("mouse_up", self.__on_mouse_up)
        self.add_event_handler("mouse_click", self.__on_mouse_click)
        self.add_event_handler("mouse_drag", self.__on_drag)
        self.add_event_handler("update", self.__on_update)
        self.add_event_handler("draw", self.__on_draw)

        self._overlay_canvas = OverlayCanvas()

    def __on_change_x(self, value):
        self.viewport_x = value * 8

    def __on_change_y(self, value):
        self.viewport_y = value * 8

    def __on_mouse_down(self, key, x, y):
        if key != pyxel.KEY_LEFT_BUTTON:
            return

        x = (x - self.x) // 8
        y = (y - self.y) // 8

        self._press_x = x
        self._press_y = y

        self._is_dragged = True
        self._is_guide_mode = False

        if self.parent.tool == TOOL_SELECT:
            self._select_x1 = self._select_x2 = x
            self._select_y1 = self._select_y2 = y
        elif self.parent.tool >= TOOL_PENCIL and self.parent.tool <= TOOL_CIRC:
            self._overlay_canvas.pix(x, y, self.parent.color)
        elif self.parent.tool == TOOL_BUCKET:
            if self._is_tilemap_mode:
                dest = pyxel.tilemap(self.parent.tilemap).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]

                data = {}
                data["tilemap"] = self.parent.tilemap
                data["pos"] = (self.viewport_x, self.viewport_y)
                data["before"] = dest.copy()

                dest = pyxel.tilemap(self.parent.tilemap).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]
            else:
                dest = pyxel.image(self.parent.image).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]

                data = {}
                data["image"] = self.parent.image
                data["pos"] = (self.viewport_x, self.viewport_y)
                data["before"] = dest.copy()

                dest = pyxel.image(self.parent.image).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]

            self._overlay_canvas.paint(x, y, self.parent.color, dest)

            data["after"] = dest.copy()
            self.parent.add_edit_history(data)

        self._last_x = x
        self._last_y = y

    def __on_mouse_up(self, key, x, y):
        if key != pyxel.KEY_LEFT_BUTTON:
            return

        self._is_dragged = False

        if self.parent.tool >= TOOL_PENCIL and self.parent.tool <= TOOL_CIRC:
            if self._is_tilemap_mode:
                dest = pyxel.tilemap(self.parent.tilemap).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]

                data = {}
                data["tilemap"] = self.parent.tilemap
                data["pos"] = (self.viewport_x, self.viewport_y)
                data["before"] = dest.copy()

                index = self._overlay_canvas.data != -1
                dest[index] = self._overlay_canvas.data[index]
                self._overlay_canvas.clear()

                data["after"] = dest.copy()
                self.parent.add_edit_history(data)
            else:
                dest = pyxel.image(self.parent.image).data[
                    self.viewport_y : self.viewport_y + 16,
                    self.viewport_x : self.viewport_x + 16,
                ]

                data = {}
                data["image"] = self.parent.image
                data["pos"] = (self.viewport_x, self.viewport_y)
                data["before"] = dest.copy()

                index = self._overlay_canvas.data != -1
                dest[index] = self._overlay_canvas.data[index]
                self._overlay_canvas.clear()

                data["after"] = dest.copy()
                self.parent.add_edit_history(data)

    def __on_mouse_click(self, key, x, y):
        if key == pyxel.KEY_RIGHT_BUTTON:
            x = self.viewport_x + (x - self.x) // 8
            y = self.viewport_y + (y - self.y) // 8

            if self._is_tilemap_mode:
                self.parent.color = pyxel.tilemap(self.parent.tilemap).data[y, x]
            else:
                self.parent.color = pyxel.image(self.parent.image).data[y, x]

    def __on_drag(self, key, x, y, dx, dy):
        if key == pyxel.KEY_LEFT_BUTTON:
            x1 = self._press_x
            y1 = self._press_y
            x2 = (x - self.x) // 8
            y2 = (y - self.y) // 8

            if self.parent.tool == TOOL_SELECT:
                x2 = min(max(x2, 0), 15)
                y2 = min(max(y2, 0), 15)
                self._select_x1, self._select_x2 = (x1, x2) if x1 < x2 else (x2, x1)
                self._select_y1, self._select_y2 = (y1, y2) if y1 < y2 else (y2, y1)
            elif self.parent.tool == TOOL_PENCIL:
                if self._is_guide_mode:
                    self._overlay_canvas.clear()
                    self._overlay_canvas.line(x1, y1, x2, y2, self.parent.color)
                else:
                    self._overlay_canvas.line(
                        self._last_x, self._last_y, x2, y2, self.parent.color
                    )
            elif self.parent.tool == TOOL_RECTB:
                self._overlay_canvas.clear()
                self._overlay_canvas.rectb(
                    x1, y1, x2, y2, self.parent.color, self._is_guide_mode
                )
            elif self.parent.tool == TOOL_RECT:
                self._overlay_canvas.clear()
                self._overlay_canvas.rect(
                    x1, y1, x2, y2, self.parent.color, self._is_guide_mode
                )
            elif self.parent.tool == TOOL_CIRCB:
                self._overlay_canvas.clear()
                self._overlay_canvas.circb(
                    x1, y1, x2, y2, self.parent.color, self._is_guide_mode
                )
            elif self.parent.tool == TOOL_CIRC:
                self._overlay_canvas.clear()
                self._overlay_canvas.circ(
                    x1, y1, x2, y2, self.parent.color, self._is_guide_mode
                )

            self._last_x = x2
            self._last_y = y2

        elif key == pyxel.KEY_RIGHT_BUTTON:
            self._drag_offset_x -= dx
            self._drag_offset_y -= dy

            if abs(self._drag_offset_x) >= 16:
                offset = self._drag_offset_x // 16
                self.viewport_x += offset * 8
                self._drag_offset_x -= offset * 16

            if abs(self._drag_offset_y) >= 16:
                offset = self._drag_offset_y // 16
                self.viewport_y += offset * 8
                self._drag_offset_y -= offset * 16

            self.viewport_x = min(max(self.viewport_x, 0), 240)
            self.viewport_y = min(max(self.viewport_y, 0), 240)

    def __on_update(self):
        self._h_scroll_bar.value = self.viewport_x // 8
        self._v_scroll_bar.value = self.viewport_y // 8

        if self._is_dragged and not self._is_guide_mode and pyxel.btn(pyxel.KEY_SHIFT):
            self._is_guide_mode = True

            x1 = self._press_x
            y1 = self._press_y
            x2 = self._last_x
            y2 = self._last_y

            if self.parent.tool == TOOL_PENCIL:
                self._overlay_canvas.clear()
                self._overlay_canvas.line(x1, y1, x2, y2, self.parent.color)
            elif self.parent.tool == TOOL_RECTB:
                self._overlay_canvas.clear()
                self._overlay_canvas.rectb(x1, y1, x2, y2, self.parent.color, True)
            elif self.parent.tool == TOOL_RECT:
                self._overlay_canvas.clear()
                self._overlay_canvas.rect(x1, y1, x2, y2, self.parent.color, True)
            elif self.parent.tool == TOOL_CIRCB:
                self._overlay_canvas.clear()
                self._overlay_canvas.circb(x1, y1, x2, y2, self.parent.color, True)
            elif self.parent.tool == TOOL_CIRC:
                self._overlay_canvas.clear()
                self._overlay_canvas.circ(x1, y1, x2, y2, self.parent.color, True)

        if (
            self.parent.tool == TOOL_SELECT
            and self._select_x1 >= 0
            and pyxel.btn(pyxel.KEY_CONTROL)
        ):
            if pyxel.btnp(pyxel.KEY_C):
                if self._is_tilemap_mode:
                    data = pyxel.tilemap(self.parent.tilemap).data
                else:
                    data = pyxel.image(self.parent.image).data

                src = data[
                    self.viewport_y
                    + self._select_y1 : self.viewport_y
                    + self._select_y2
                    + 1,
                    self.viewport_x
                    + self._select_x1 : self.viewport_x
                    + self._select_x2
                    + 1,
                ]
                self._copy_buffer = src.copy()
            elif self._copy_buffer is not None and pyxel.btnp(pyxel.KEY_V):
                x1 = self.viewport_x + self._select_x1
                y1 = self.viewport_y + self._select_y1

                height, width = self._copy_buffer.shape
                width -= max(self._select_x1 + width - 16, 0)
                height -= max(self._select_y1 + height - 16, 0)

                if self._is_tilemap_mode:
                    data = pyxel.tilemap(self.parent.tilemap).data
                else:
                    data = pyxel.image(self.parent.image).data

                dest = data[y1 : y1 + height, x1 : x1 + width]
                dest[:, :] = self._copy_buffer[:height, :width]

    def __on_draw(self):
        if self._is_tilemap_mode:
            pyxel.bltm(
                self.x,
                self.y,
                self.parent.image,
                self.parent.tilemap,
                self.viewport_x,
                self.viewport_y,
                16,
                16,
            )

            for i in range(16):
                y = self.y + i * 8
                for j in range(16):
                    x = self.x + j * 8

                    val = self._overlay_canvas.data[i, j]
                    if val >= 0:
                        sx = (val % 32) * 8
                        sy = (val // 32) * 8
                        pyxel.blt(x, y, self.parent.image, sx, sy, 8, 8)
        else:
            for i in range(16):
                y = self.y + i * 8
                for j in range(16):
                    x = self.x + j * 8

                    val = self._overlay_canvas.data[i, j]
                    if val >= 0:
                        col = self._overlay_canvas.data[i, j]
                    else:
                        data = pyxel.image(self.parent.image).data
                        col = data[self.viewport_y + i, self.viewport_x + j]

                    pyxel.rect(x, y, x + 7, y + 7, col)

        pyxel.line(self.x, self.y + 63, self.x + 127, self.y + 63, 1)
        pyxel.line(self.x + 63, self.y, self.x + 63, self.y + 127, 1)

        if self.parent.tool == TOOL_SELECT and self._select_x1 >= 0:
            x1 = self._select_x1 * 8 + 12
            y1 = self._select_y1 * 8 + 17
            x2 = self._select_x2 * 8 + 19
            y2 = self._select_y2 * 8 + 24

            pyxel.rectb(x1, y1, x2, y2, 0)
            pyxel.rectb(x1 + 1, y1 + 1, x2 - 1, y2 - 1, 15)
            pyxel.rectb(x1 + 2, y1 + 2, x2 - 2, y2 - 2, 0)