2024-03-02 13:04:24 +01:00
|
|
|
import base64
|
2024-03-03 17:48:11 +01:00
|
|
|
import platform
|
2024-03-02 12:28:14 +01:00
|
|
|
import tkinter
|
|
|
|
|
|
|
|
GRID_COLOR = "#808080"
|
|
|
|
GRID_GUIDE_COLOR = (128, 128, 255)
|
|
|
|
|
|
|
|
class EditorCanvas(tkinter.Canvas):
|
|
|
|
def __init__(self,project,*args,**kwargs):
|
|
|
|
super().__init__(*args,**kwargs)
|
|
|
|
self.project=project
|
|
|
|
self.grid_size = 32
|
|
|
|
self.current_char=33
|
|
|
|
self.current_char_pixels=[]
|
2024-03-02 13:04:24 +01:00
|
|
|
self.current_char_modified=False
|
2024-03-02 12:28:14 +01:00
|
|
|
self.width=0
|
|
|
|
self.height=0
|
2024-03-03 17:27:39 +01:00
|
|
|
self.prev_mouse_x=0
|
|
|
|
self.prev_mouse_y=0
|
|
|
|
self.view_x=0
|
|
|
|
self.view_y=0
|
2024-03-02 12:28:14 +01:00
|
|
|
self.bind("<B1-Motion>",self.handle_draw)
|
|
|
|
self.bind("<Button-1>",self.handle_draw)
|
2024-03-03 17:27:39 +01:00
|
|
|
self.bind("<Button-3>",self.handle_move_start)
|
|
|
|
self.bind("<B3-Motion>",self.handle_move)
|
2024-03-03 17:48:11 +01:00
|
|
|
if platform.system()=="Windows" or platform.system()=="Darwin":
|
|
|
|
self.bind("<MouseWheel>",lambda event: self.handle_zoom(event.delta))
|
|
|
|
else:
|
|
|
|
# This will probably work only on X11
|
|
|
|
self.bind("<Button-4>",lambda _: self.handle_zoom(1))
|
|
|
|
self.bind("<Button-5>",lambda _: self.handle_zoom(-1))
|
2024-03-02 12:28:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
def draw(self):
|
|
|
|
self.delete("all")
|
|
|
|
|
|
|
|
# draw grid
|
|
|
|
for x in range(self.project.char_res[0] + 1):
|
2024-03-03 17:27:39 +01:00
|
|
|
x = x * self.grid_size+self.view_x
|
|
|
|
self.create_line((x,self.view_y),(x,self.view_y+self.height),width=1,fill=GRID_COLOR)
|
2024-03-02 12:28:14 +01:00
|
|
|
for y in range(self.project.char_res[1] + 1):
|
2024-03-03 17:27:39 +01:00
|
|
|
y = y * self.grid_size+self.view_y
|
|
|
|
self.create_line((self.view_x,y),(self.view_x+self.width,y),width=1,fill=GRID_COLOR)
|
2024-03-02 12:28:14 +01:00
|
|
|
|
2024-03-02 13:04:24 +01:00
|
|
|
if self.project.does_char_exist(self.current_char) or self.current_char_modified:
|
2024-03-02 12:28:14 +01:00
|
|
|
for i in range(len(self.current_char_pixels)):
|
2024-03-03 17:27:39 +01:00
|
|
|
x=i//self.project.char_res[1]*self.grid_size+self.view_x
|
|
|
|
y=i%self.project.char_res[1]*self.grid_size+self.view_y
|
2024-03-02 12:28:14 +01:00
|
|
|
if self.current_char_pixels[i]>0:
|
|
|
|
self.create_rectangle((x,y),(x+self.grid_size,y+self.grid_size),fill="white")
|
|
|
|
else:
|
2024-03-03 17:27:39 +01:00
|
|
|
self.create_line((self.view_x,self.view_y),(self.view_x+self.width,self.view_y+self.height),width=3,fill="red")
|
|
|
|
self.create_line((self.view_x+self.width,self.view_y),(self.view_x,self.view_y+self.height),width=3,fill="red")
|
2024-03-02 12:28:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
def handle_draw(self,event):
|
2024-03-02 13:04:24 +01:00
|
|
|
self.current_char_modified=True
|
2024-03-02 12:28:14 +01:00
|
|
|
pixel_pos=self.cursor_pos_to_pixel((event.x,event.y))
|
2024-03-03 17:27:39 +01:00
|
|
|
if not pixel_pos:
|
|
|
|
return
|
2024-03-02 13:04:24 +01:00
|
|
|
self.current_char_pixels[pixel_pos[0]*self.project.char_res[1]+pixel_pos[1]]=1
|
2024-03-02 12:28:14 +01:00
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
2024-03-03 17:27:39 +01:00
|
|
|
def handle_move_start(self,event):
|
|
|
|
self.prev_mouse_x=event.x
|
|
|
|
self.prev_mouse_y=event.y
|
|
|
|
|
|
|
|
|
|
|
|
def handle_move(self,event):
|
|
|
|
self.view_x+=event.x-self.prev_mouse_x
|
|
|
|
self.view_y+=event.y-self.prev_mouse_y
|
|
|
|
self.prev_mouse_x=event.x
|
|
|
|
self.prev_mouse_y=event.y
|
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
2024-03-03 17:48:11 +01:00
|
|
|
def handle_zoom(self,delta):
|
|
|
|
self.grid_size+=delta
|
|
|
|
self.width=self.project.char_res[0]*self.grid_size
|
|
|
|
self.height=self.project.char_res[1]*self.grid_size
|
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
2024-03-02 12:28:14 +01:00
|
|
|
def load_char(self):
|
2024-03-02 13:04:24 +01:00
|
|
|
self.current_char_modified=False
|
|
|
|
if not self.project.does_char_exist(self.current_char):
|
|
|
|
for x in range(len(self.current_char_pixels)):
|
|
|
|
self.current_char_pixels[x]=0
|
|
|
|
return
|
|
|
|
base64_data=self.project.chars[chr(self.current_char)]
|
|
|
|
pixels=base64.b64decode(base64_data.encode("ascii"))
|
|
|
|
pixel_index=0
|
|
|
|
for pixel in pixels:
|
|
|
|
if pixel_index>=len(self.current_char_pixels):
|
|
|
|
break
|
|
|
|
for x in range(8):
|
|
|
|
if pixel_index>=len(self.current_char_pixels):
|
|
|
|
break
|
|
|
|
self.current_char_pixels[pixel_index]=(pixel>>(7-x))&1
|
|
|
|
pixel_index+=1
|
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
|
|
|
def save_char(self):
|
2024-03-02 22:21:48 +01:00
|
|
|
if not self.current_char_modified:
|
|
|
|
return
|
|
|
|
|
|
|
|
empty=True
|
|
|
|
for x in range(len(self.current_char_pixels)):
|
|
|
|
if self.current_char_pixels[x]>0:
|
|
|
|
empty=False
|
|
|
|
break
|
|
|
|
if empty:
|
|
|
|
if self.project.does_char_exist(self.current_char):
|
|
|
|
del(self.project.chars[chr(self.current_char)])
|
|
|
|
return
|
|
|
|
|
|
|
|
packed_data=[]
|
|
|
|
bit_counter=0
|
|
|
|
current_value=0
|
|
|
|
for pixel in self.current_char_pixels:
|
|
|
|
if bit_counter==8:
|
|
|
|
packed_data.append(current_value)
|
|
|
|
bit_counter=0
|
|
|
|
current_value=0
|
2024-03-03 17:06:37 +01:00
|
|
|
current_value|=(pixel<<(7-bit_counter))
|
2024-03-02 22:21:48 +01:00
|
|
|
bit_counter+=1
|
2024-03-03 17:06:37 +01:00
|
|
|
if bit_counter>1:
|
|
|
|
packed_data.append(current_value)
|
2024-03-02 22:21:48 +01:00
|
|
|
|
|
|
|
self.project.chars[chr(self.current_char)]=base64.b64encode(bytes(packed_data)).decode("ascii")
|
2024-03-02 12:28:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
def prev_glyph(self):
|
2024-03-02 22:21:48 +01:00
|
|
|
self.save_char()
|
2024-03-02 12:28:14 +01:00
|
|
|
self.current_char-=1
|
|
|
|
self.keep_current_char_in_bounds()
|
|
|
|
self.load_char()
|
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
|
|
|
def next_glyph(self):
|
2024-03-02 22:21:48 +01:00
|
|
|
self.save_char()
|
2024-03-02 12:28:14 +01:00
|
|
|
self.current_char+=1
|
|
|
|
self.keep_current_char_in_bounds()
|
|
|
|
self.load_char()
|
|
|
|
self.draw()
|
|
|
|
|
|
|
|
|
|
|
|
def cursor_pos_to_pixel(self,pos):
|
2024-03-03 17:27:39 +01:00
|
|
|
pixel_pos = ((pos[0]-self.view_x) // self.grid_size, (pos[1]-self.view_y) // self.grid_size)
|
|
|
|
if pixel_pos[0]<0 or pixel_pos[0]>=self.project.char_res[0] or pixel_pos[1]<0 or pixel_pos[1]>=self.project.char_res[1]:
|
|
|
|
return None
|
2024-03-02 12:28:14 +01:00
|
|
|
return pixel_pos
|
|
|
|
|
|
|
|
|
|
|
|
def keep_current_char_in_bounds(self):
|
|
|
|
if self.current_char<0:
|
|
|
|
self.current_char=0
|
|
|
|
elif self.current_char>99999:
|
|
|
|
self.current_char=99999
|
|
|
|
|
|
|
|
|
|
|
|
def after_project_load(self):
|
|
|
|
self.width=self.project.char_res[0]*self.grid_size
|
|
|
|
self.height=self.project.char_res[1]*self.grid_size
|
|
|
|
self.current_char_pixels=[0 for _ in range(self.project.char_res[0]*self.project.char_res[1])]
|
|
|
|
self.load_char()
|
|
|
|
|