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