sprite-editor.py 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. # THIS IS A SOURCE CODE FILE FROM I'M NOT EVEN HUMAN THE GAME.
  2. # IT COULD BE USED IN A DIFFERENT PIECE OF SOFTWARE ( LIKE A
  3. # DIFFERENT GAME ), BUT IT WAS ORIGINALLY WRITTEN FOR I'M NOT
  4. # EVEN HUMAN THE GAME.
  5. # THE DEVELOPERS OF THE GAME ARE : (C) J.Y.AMIHUD, AYYZEE AND
  6. # OTHER CONTRIBUTORS. THIS AND OTHER FILES IN THIS GAME,
  7. # UNLESS SPECIFICALLY NOTED, COULD BE USED UNDER THE TERMS OF
  8. # GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER VERSION.
  9. import os
  10. import json
  11. import time
  12. import random
  13. import datetime # For the FPS meter mainly
  14. # GTK module ( Graphical interface
  15. import gi
  16. gi.require_version('Gtk', '3.0')
  17. from gi.repository import Gtk
  18. from gi.repository import Gdk
  19. from gi.repository import GLib
  20. from gi.repository import GdkPixbuf
  21. import cairo
  22. import threading
  23. from subprocess import *
  24. from PIL import Image
  25. from modules import ui
  26. win = Gtk.Window()
  27. win.set_size_request(600, 600)
  28. win.connect("destroy", Gtk.main_quit)
  29. # Hack
  30. win.images = {}
  31. win.current = {"frame":0}
  32. win.FPS = 60
  33. win.zoom = 2
  34. ############# TITLE BAR TOOLBAR #######
  35. pannel = Gtk.HeaderBar()
  36. pannel.set_show_close_button(True)
  37. win.set_titlebar(pannel)
  38. def do_launch_game(w):
  39. os.system("python3 play.py")
  40. launch = Gtk.Button("Launch Game Test")
  41. launch.connect("clicked", do_launch_game)
  42. pannel.pack_start(launch)
  43. selected_asset = {"path":"",
  44. "image":"",
  45. "frames":[]}
  46. paned = Gtk.HPaned()
  47. win.add(paned)
  48. #############################################################################
  49. # #
  50. # #
  51. # THE ASSET SELECTOR THINGY!!! #
  52. # #
  53. # The tree view to select our asset #
  54. # #
  55. # #
  56. #############################################################################
  57. def save_sprite(name):
  58. # First we need to find out how big is the biggest cell
  59. # And all kinds of other things.
  60. largestx = 0
  61. largesty = 0
  62. all_sprites = 0
  63. most_frames = 0
  64. empties = []
  65. for i in win.images[name]:
  66. data = win.images[name][i]
  67. print()
  68. print(i, data)
  69. if type(data) == list and len(data) and type(data[0]) == dict:
  70. if "title" not in data[0]:
  71. empties.append(i)
  72. for i in empties:
  73. del win.images[name][i]
  74. for i in win.images[name]:
  75. data = win.images[name][i]
  76. if type(data) == list and len(data) and type(data[0]) == dict:
  77. all_sprites += 1
  78. if len(data) > most_frames:
  79. most_frames = len(data)
  80. for frame in data:
  81. if frame["binary"].get_width() > largestx:
  82. largestx = frame["binary"].get_width()
  83. if frame["binary"].get_height() > largesty:
  84. largesty = frame["binary"].get_height()
  85. print(all_sprites)
  86. # Now we need to figured all of it out. Let's calculate and create
  87. # the canvas size for the final sprite
  88. canvasx = int(largestx * all_sprites)
  89. canvasy = int(largesty * most_frames)
  90. canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32,
  91. canvasx,
  92. canvasy)
  93. brush = cairo.Context(canvas)
  94. metadata = {}
  95. metadata["title"] = win.images[name].get("title", "")
  96. metadata["authors"] = win.images[name].get("authors", [])
  97. metadata["licenses"] = win.images[name].get("licenses", [])
  98. metadata["menu_color"] = win.images[name].get("menu_color", "#000000")
  99. metadata["resolution"] = [ all_sprites, most_frames ]
  100. n = -1
  101. for i in win.images[name]:
  102. data = win.images[name][i]
  103. if type(data) == list and len(data) and type(data[0]) == dict:
  104. n = n + 1
  105. for ind, frame in enumerate(data):
  106. brush.set_source_surface(frame["binary"],
  107. int(largestx*n),
  108. int(largesty*ind))
  109. brush.paint()
  110. metadata[str(n)+":"+str(ind)] = {}
  111. for stuff in frame:
  112. if stuff != "binary":
  113. metadata[str(n)+":"+str(ind)][stuff] = frame[stuff]
  114. empties = []
  115. for i in metadata:
  116. if not metadata[i]:
  117. empties.append(i)
  118. for i in empties:
  119. del metadata[i]
  120. canvas.write_to_png(name)
  121. print("Sprite image was saved:", name)
  122. with open(name.replace(".png", ".json"), 'w') as f:
  123. json.dump(metadata, f, indent=4)
  124. win.images = {}
  125. update_tree()
  126. def do_revert(w=False):
  127. win.images = {}
  128. update_tree()
  129. for i in frames_box.get_children():
  130. i.destroy()
  131. selected_asset["path"] = ""
  132. selected_asset["image"] = ""
  133. test_save = Gtk.Button("Re-Load")
  134. test_save.connect("clicked", do_revert)
  135. pannel.pack_start(test_save)
  136. #############################################################################
  137. # #
  138. # #
  139. # THE ASSET SELECTOR THINGY!!! #
  140. # #
  141. # The tree view to select our asset #
  142. # #
  143. # #
  144. #############################################################################
  145. def do_test_save(w):
  146. save_sprite(selected_asset["path"])
  147. test_save = Gtk.Button("Save Current Asset")
  148. test_save.connect("clicked", do_test_save)
  149. pannel.pack_start(test_save)
  150. def on_select(w):
  151. for i in frames_box.get_children():
  152. i.destroy()
  153. selected_frame["is"] = None
  154. (model, pathlist) = w.get_selection().get_selected_rows()
  155. for path in pathlist :
  156. tree_iter = model.get_iter(path)
  157. thepath = model.get_value(tree_iter,1)
  158. image = model.get_value(tree_iter,2)
  159. datapath = thepath+":"+image
  160. print(datapath)
  161. selected_asset["path"] = thepath
  162. selected_asset["image"] = image
  163. for n, i in enumerate(data_frames.get(datapath, [])):
  164. toggle = Gtk.ToggleButton("Frame "+str(n))
  165. toggle.connect("clicked", on_toggle_frame, n)
  166. toggle.set_relief(Gtk.ReliefStyle.NONE)
  167. frames_box.pack_start(toggle,0,0,0)
  168. frames_box.show_all()
  169. data_frames = {}
  170. treebox = Gtk.HBox()
  171. paned.add(treebox)
  172. def update_tree():
  173. for i in treebox.get_children():
  174. i.destroy()
  175. inds = list(data_frames.keys())
  176. for i in inds:
  177. del data_frames[i]
  178. data_tree_store = Gtk.TreeStore(str, str, str)
  179. tree = Gtk.TreeView(data_tree_store)
  180. tree.connect("cursor-changed", on_select)
  181. treescrl = Gtk.ScrolledWindow()
  182. treescrl.set_size_request(300,300)
  183. treescrl.add(tree)
  184. treebox.pack_start(treescrl, 1,1,0)
  185. key_column = Gtk.TreeViewColumn("Select Sprite")
  186. text_col = Gtk.CellRendererText()
  187. text_col.set_property("editable", True)
  188. key_column.pack_start(text_col, True)
  189. key_column.add_attribute(text_col, "text", 0)
  190. tree.append_column(key_column)
  191. def load_image_data(path):
  192. try:
  193. with open(path+".json") as f:
  194. data = json.load(f)
  195. except Exception as e:
  196. data = {}
  197. return data
  198. def populate(treeStore, data, parent=None):
  199. data = list(data)
  200. # Folders
  201. for i in data[0][1]:
  202. # Excluding the Worlds folder
  203. if i == "worlds":
  204. continue
  205. this = treeStore.append(parent, [i, data[0][0]+"/"+i, ""])
  206. populate(treeStore, os.walk(data[0][0]+"/"+i), this)
  207. # files
  208. for i in data[0][2]:
  209. if i.endswith(".png"):
  210. name = i[:-4]
  211. path = data[0][0]+"/"+i[:-4]
  212. metadata = load_image_data(path)
  213. name = metadata.get("title", name)
  214. this = treeStore.append(parent, [name, data[0][0]+"/"+i, ""])
  215. # TODO: Somehow to expland back to where it was after saving.
  216. res = metadata.get("resolution", [1,1])
  217. if res[0] or res[1]:
  218. for x in range(res[0]):
  219. for y in range(res[1]):
  220. index = str(x)+":"+str(y)
  221. index = metadata.get(index, {"title":index}).get("title", index)
  222. if index == str(x)+":"+str(y):
  223. continue
  224. if data[0][0]+"/"+i+":"+index not in data_frames:
  225. data_frames[data[0][0]+"/"+i+":"+index] = [str(x)+":"+str(y)]
  226. treeStore.append(this, [index, data[0][0]+"/"+i, index])
  227. else:
  228. data_frames[data[0][0]+"/"+i+":"+index].append(str(x)+":"+str(y))
  229. populate(data_tree_store, os.walk("assets"), None)
  230. treebox.show_all()
  231. update_tree()
  232. second_paned = Gtk.HPaned()
  233. paned.add(second_paned)
  234. #############################################################################
  235. # #
  236. # #
  237. # THE FRAME SELECTOR THINGY!!! #
  238. # #
  239. # The tree view to select our frames #
  240. # #
  241. # #
  242. #############################################################################
  243. frames_box = Gtk.VBox()
  244. frames_box_box = Gtk.VBox()
  245. frames_box_box.pack_start(Gtk.Label("Sprite Frames"), 0,0,5)
  246. frames_scrl = Gtk.ScrolledWindow()
  247. frames_scrl.add(frames_box)
  248. frames_box_box.pack_end(frames_scrl, 1,1,0)
  249. frames_scrl.set_size_request(300,300)
  250. second_paned.add(frames_box_box)
  251. selected_frame = {"is":None}
  252. def on_toggle_frame(w, n):
  253. if w.get_active():
  254. for m, i in enumerate(frames_box.get_children()):
  255. if m != n:
  256. i.set_active(False)
  257. selected_frame["is"] = n
  258. else:
  259. selected_frame["is"] = None
  260. #############################################################################
  261. # #
  262. # #
  263. # THE PREVIEW THINGY!!! #
  264. # #
  265. # The part where you actually see the image. #
  266. # #
  267. # #
  268. #############################################################################
  269. last_third = Gtk.VBox()
  270. second_paned.add(last_third)
  271. def do_preview_pad(d, main_layer):
  272. win.current["frame"] += 1
  273. w = d.get_allocated_width()
  274. h = d.get_allocated_height()
  275. mx = d.get_pointer()[0]
  276. my = d.get_pointer()[1]
  277. # THE CENTER GRID
  278. main_layer.set_source_rgba(0.7,0.7,0.7,1)
  279. main_layer.set_line_width(3)
  280. main_layer.move_to( w/2, h/2-10 )
  281. main_layer.line_to( w/2, h/2+10 )
  282. main_layer.stroke()
  283. main_layer.set_line_width(1)
  284. main_layer.set_source_rgba(0.2,0.2,0.2,1)
  285. main_layer.move_to( w/2, h/2-10 )
  286. main_layer.line_to( w/2, h/2+10 )
  287. main_layer.stroke()
  288. main_layer.set_line_width(3)
  289. main_layer.set_source_rgba(0.7,0.7,0.7,1)
  290. main_layer.move_to( w/2-10, h/2 )
  291. main_layer.line_to( w/2+10, h/2 )
  292. main_layer.stroke()
  293. main_layer.set_line_width(1)
  294. main_layer.set_source_rgba(0.2,0.2,0.2,1)
  295. main_layer.move_to( w/2-10, h/2 )
  296. main_layer.line_to( w/2+10, h/2 )
  297. main_layer.stroke()
  298. main_layer.set_line_width(3)
  299. # THE PREVIEW BLOCK
  300. bh = 34 *win.zoom
  301. bw = 63 *win.zoom
  302. points = [
  303. [w/2, h/2+bh/2+win.zoom],
  304. [w/2+bw/2, h/2+win.zoom],
  305. [w/2+bw, h/2+bh/2+win.zoom],
  306. [w/2+bw/2, h/2+bh+win.zoom],
  307. ]
  308. main_layer.move_to( *points[-1] )
  309. main_layer.set_source_rgba(0.7,0.7,0.7,1)
  310. for i in points:
  311. main_layer.line_to( *i )
  312. main_layer.stroke()
  313. main_layer.move_to( *points[-1] )
  314. for i in points:
  315. main_layer.line_to( *i )
  316. main_layer.set_line_width(1)
  317. main_layer.set_source_rgba(0.2,0.2,0.2,1)
  318. main_layer.stroke()
  319. main_layer.set_line_width(3)
  320. asset = selected_asset.get("path", "")
  321. image = selected_asset.get("image", "")
  322. try:
  323. if not sprite_name.is_focus():
  324. sprite_name.set_text(win.images[asset][image][0]["title"])
  325. if not asset_name.is_focus():
  326. asset_name.set_text(win.images[asset]["title"])
  327. except:
  328. pass
  329. #### MOVE OFFSET OF THE IMAGE ###
  330. posx = w/2/win.zoom
  331. posy = h/2/win.zoom
  332. offx, offy = 0 , 0
  333. sizex, sizey = 0,0
  334. try:
  335. offx = win.images[asset][image]
  336. if selected_frame["is"] == None:
  337. offx = offx[0]
  338. else:
  339. offx = offx[selected_frame["is"]]
  340. sizex = offx["binary"].get_width()
  341. sizey = offx["binary"].get_height()
  342. collision_toggle.set_active(offx.get("collision", True))
  343. dynamic_toggle.set_active(offx.get("dynamic", False))
  344. main_character_toggle.set_active(offx.get("main_character", False))
  345. main_character_toggle.set_visible(offx.get("dynamic", False))
  346. ramp_toggle.set_active(offx.get("ramp", False))
  347. ramp_toggle.set_visible(not offx.get("dynamic", False))
  348. damage_toggle.set_active(offx.get("damage", False))
  349. if not damage_amount_spin.is_focus():
  350. damage_amount_spin.set_value(offx.get("damage_amount", 0.01))
  351. damage_amount_box.set_visible(offx.get("damage", False))
  352. invisible_toggle.set_active(offx.get("invisible", False))
  353. offx, offy = offx["offset"]
  354. if not xentry.is_focus():
  355. xentry.set_sensitive(True)
  356. xentry.set_value(offx)
  357. if not yentry.is_focus():
  358. yentry.set_sensitive(True)
  359. yentry.set_value(offy)
  360. except Exception as e:
  361. xentry.set_sensitive(False)
  362. yentry.set_sensitive(False)
  363. sizex = sizex * win.zoom
  364. sizey = sizey * win.zoom
  365. if 0 < mx < w and 0 < my < h:
  366. main_layer.set_source_rgba(0.7,0.7,0.7,1)
  367. main_layer.rectangle((posx+offx)*win.zoom, (posy+offy)*win.zoom, sizex, sizey)
  368. main_layer.stroke()
  369. main_layer.set_line_width(1)
  370. main_layer.set_source_rgba(0.2,0.2,0.2,1)
  371. main_layer.rectangle((posx+offx)*win.zoom, (posy+offy)*win.zoom, sizex, sizey)
  372. main_layer.stroke()
  373. main_layer.set_line_width(3)
  374. # If dragging:
  375. if win.offseter["pressed"]:
  376. try:
  377. if selected_frame["is"] != None:
  378. offx = offx + (mx-win.offseter["previous"][0])/win.zoom
  379. offy = offy + (my-win.offseter["previous"][1])/win.zoom
  380. win.images[asset][image][selected_frame["is"]]["offset"] = [int(offx), int(offy)]
  381. else:
  382. for frame in win.images[asset][image]:
  383. offx, offy = frame["offset"]
  384. offx = offx + (mx-win.offseter["previous"][0])/win.zoom
  385. offy = offy + (my-win.offseter["previous"][1])/win.zoom
  386. frame["offset"] = [int(offx), int(offy)]
  387. # TODO: Find something that's not as fucked.
  388. zoom_minus.grab_focus()
  389. except Exception as e:
  390. print(e)
  391. win.offseter["previous"] = [mx, my]
  392. main_layer.scale(win.zoom,
  393. win.zoom)
  394. main_layer.set_antialias(cairo.ANTIALIAS_NONE)
  395. p = main_layer.get_source()
  396. p.set_filter(cairo.FILTER_NEAREST)
  397. if asset and image:
  398. try:
  399. ui.image(win, main_layer,
  400. posx,
  401. posy,
  402. asset, image, color=False, offset=True, frame=selected_frame["is"])
  403. except Exception as e:
  404. pass
  405. # SENSITIVE OR NOT BASED ON WHETHER THERE IS IMAGE SELECTED
  406. test_save.set_sensitive(True)
  407. asset_name.set_sensitive(True)
  408. sprite_name.set_sensitive(True)
  409. add_sprite.set_sensitive(True)
  410. remove_sprite.set_sensitive(True)
  411. add_frame.set_sensitive(True)
  412. remove_frame.set_sensitive(True)
  413. change_frame.set_sensitive(True)
  414. copy_frame.set_sensitive(True)
  415. licenses_editor.set_sensitive(True)
  416. authors_editor.set_sensitive(True)
  417. extra_button.set_sensitive(True)
  418. asset_color.set_sensitive(True)
  419. licenses = win.images[asset]["licenses"]
  420. if licenses_get() != licenses:
  421. print("Update Licenses")
  422. licenses_updator(licenses)
  423. authors = win.images[asset]["authors"]
  424. if authors_get() != authors:
  425. authors_updator(authors)
  426. def hex_to_rgb(value):
  427. value = value.lstrip('#')
  428. lv = len(value)
  429. return list(int(value[i:i+lv//3], 16) for i in range(0, lv, lv//3))
  430. color = win.images[asset]["menu_color"]
  431. r,g,b = hex_to_rgb(color)
  432. color = []
  433. for i in (r,g,b):
  434. color.append(i*255)
  435. asset_color.set_color(Gdk.Color(*color))
  436. else:
  437. test_save.set_sensitive(False)
  438. asset_name.set_sensitive(False)
  439. sprite_name.set_sensitive(False)
  440. add_sprite.set_sensitive(False)
  441. remove_sprite.set_sensitive(False)
  442. add_frame.set_sensitive(False)
  443. remove_frame.set_sensitive(False)
  444. change_frame.set_sensitive(False)
  445. copy_frame.set_sensitive(False)
  446. licenses_editor.set_sensitive(False)
  447. authors_editor.set_sensitive(False)
  448. extra_button.set_sensitive(False)
  449. asset_color.set_sensitive(False)
  450. licenses_updator([])
  451. authors_updator([])
  452. asset_name.set_text("")
  453. sprite_name.set_text("")
  454. preview_pad.queue_draw()
  455. event_box = Gtk.EventBox()
  456. preview_pad = Gtk.DrawingArea()
  457. event_box.add(preview_pad)
  458. event_box.set_size_request(200, 200)
  459. last_third.pack_start(event_box, 1,1,1)
  460. preview_pad.connect("draw", do_preview_pad)
  461. # We need some logic for the offset editor ( which is moving the image with
  462. # the cursor in the preview pad )
  463. win.offseter = {
  464. "pressed": False,
  465. "previous":[0,0]}
  466. def button_press(w, e):
  467. win.offseter["pressed"] = True
  468. win.offseter["previous"] = [e.x, e.y]
  469. def button_release(w, e):
  470. win.offseter["pressed"] = False
  471. event_box.connect("button-press-event", button_press)
  472. event_box.connect("button-release-event", button_release)
  473. last_third.pack_start(Gtk.HSeparator(), 0,0,10)
  474. def on_add_image(w, kind="Frame"):
  475. dialogWindow = Gtk.Dialog(kind,
  476. buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  477. Gtk.STOCK_OK, Gtk.ResponseType.OK),
  478. )
  479. box = dialogWindow.get_content_area()
  480. if kind == "Asset":
  481. is_asset_folder_skip = False
  482. folder_path = selected_asset.get("path")
  483. if folder_path.endswith(".png"):
  484. is_asset_folder_skip = True
  485. folder_path = folder_path[:folder_path.rfind("/")]
  486. assbox = Gtk.HBox()
  487. assbox.pack_start(Gtk.Label(folder_path+"/"), 0,0,0)
  488. asset_file_name = Gtk.Entry()
  489. asset_file_name.set_text("asset_file_name")
  490. assbox.pack_start(asset_file_name, 0,0,0)
  491. assbox.pack_start(Gtk.Label(".png"), 0,0,0)
  492. box.pack_start(assbox, 0,0,10)
  493. ######### WE WILL TRY TO GET IMAGE FROM CLIPBOARD #######
  494. clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
  495. im = clipboard.wait_for_image()
  496. if not im:
  497. box.pack_start(Gtk.Label("[No IMAGE IN CLIPBOARD]"), 0,0,10)
  498. else:
  499. box.pack_start(Gtk.Image.new_from_pixbuf(im), 0,0,0)
  500. box.show_all()
  501. response = dialogWindow.run()
  502. if response == Gtk.ResponseType.OK and im:
  503. asset = selected_asset.get("path")
  504. image = selected_asset.get("image")
  505. Px = im.get_width()
  506. Py = im.get_height()
  507. Pc = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
  508. Pb = cairo.Context(Pc)
  509. Gdk.cairo_set_source_pixbuf( Pb, im, 0, 0)
  510. Pb.paint()
  511. if kind == "Frame":
  512. ref = win.images[asset][image][-1].copy()
  513. ref["binary"] = Pc
  514. win.images[asset][image].append(ref)
  515. n = len(win.images[asset][image])-1
  516. toggle = Gtk.ToggleButton("Frame "+str(n))
  517. toggle.connect("clicked", on_toggle_frame, n)
  518. toggle.set_relief(Gtk.ReliefStyle.NONE)
  519. frames_box.pack_start(toggle,0,0,0)
  520. frames_box.show_all()
  521. elif kind == "ChangeFrame":
  522. cf = selected_frame["is"]
  523. if cf == None:
  524. cf = 0
  525. win.images[asset][image][cf]["binary"] = Pc
  526. elif kind == "Sprite":
  527. good = "qwertyuiopasdfghjklzxcvbnm1234567890"
  528. sprite_id = ""
  529. for i in range(20):
  530. sprite_id = sprite_id + random.choice(good)
  531. win.images[asset][sprite_id] = [{"binary":Pc,
  532. "title":"New Sprite Name",
  533. "offset":[0,0]}]
  534. tree = list(treebox.get_children())[0].get_child()
  535. tree_store = tree.get_model()
  536. tree_path = str(tree.get_cursor().path)
  537. tree_path = tree_path[:tree_path.rfind(":")]
  538. tree_iter = tree_store.get_iter(tree_path)
  539. new = tree_store.insert(tree_iter, 0)
  540. tree_store.set_value(new, 0,"New Sprite Name")
  541. tree_store.set_value(new, 1, asset)
  542. tree_store.set_value(new, 2, sprite_id)
  543. tree.set_cursor(tree_path)
  544. # datapath thingy for frame selector
  545. fpath = asset+":"+sprite_id
  546. data_frames[fpath] = [""]
  547. elif kind == "Asset":
  548. good = "qwertyuiopasdfghjklzxcvbnm1234567890"
  549. sprite_id = ""
  550. for i in range(20):
  551. sprite_id = sprite_id + random.choice(good)
  552. asset = folder_path+"/"+asset_file_name.get_text()+".png"
  553. win.images[asset] = {
  554. sprite_id:[{"binary":Pc,
  555. "title":"New Sprite Name",
  556. "offset":[0,0]}],
  557. "title":asset_file_name.get_text(),
  558. "authors":[],
  559. "licenses":[],
  560. "menu_color":"#000000"
  561. }
  562. # datapath thingy for frame selector
  563. fpath = asset+":"+sprite_id
  564. data_frames[fpath] = [""]
  565. tree = list(treebox.get_children())[0].get_child()
  566. tree_store = tree.get_model()
  567. tree_path = str(tree.get_cursor().path)
  568. if is_asset_folder_skip:
  569. tree_path = tree_path[:tree_path.rfind(":")]
  570. tree_iter = tree_store.get_iter(tree_path)
  571. new = tree_store.insert(tree_iter, 0)
  572. tree_store.set_value(new, 0, asset_file_name.get_text())
  573. tree_store.set_value(new, 1, asset)
  574. tree_store.set_value(new, 2, "")
  575. tree_path = tree_path + ":0"
  576. tree_iter = tree_store.get_iter(tree_path)
  577. new = tree_store.insert(tree_iter, 0)
  578. tree_store.set_value(new, 0, "New Sprite Name" )
  579. tree_store.set_value(new, 1, asset)
  580. tree_store.set_value(new, 2, sprite_id )
  581. tree.set_cursor(tree_path)
  582. dialogWindow.destroy()
  583. def tags_editor(win, data, return_edit_functions=False, auto_fill=[]):
  584. hack = {}
  585. hack["data"] = data
  586. def update(new_data):
  587. hack["data"] = new_data
  588. for i in tagsbox.get_children():
  589. i.destroy()
  590. for tag in new_data:
  591. add_tag(tag)
  592. def get():
  593. return hack["data"]
  594. tagscont = Gtk.HBox()
  595. tagscrl = Gtk.ScrolledWindow()
  596. tagscrl.set_size_request(40,40)
  597. tagscont.pack_start(tagscrl, True, True, 0)
  598. tagsbox = Gtk.HBox()
  599. tagscrl.add_with_viewport(tagsbox)
  600. def add_tag(tag):
  601. if not tag:
  602. return
  603. if tag not in hack["data"]:
  604. hack["data"].append(tag)
  605. tagb = Gtk.HBox()
  606. tagb.pack_start(Gtk.Label(" "+tag+" "), False, False, 0)
  607. def kill(w):
  608. tagb.destroy()
  609. hack["data"].remove(tag)
  610. tagk = Gtk.Button("-")
  611. tagk.connect("clicked", kill)
  612. tagk.set_relief(Gtk.ReliefStyle.NONE)
  613. tagb.pack_start(tagk, False, False, 0)
  614. tagb.pack_start(Gtk.VSeparator(), False, False, 5)
  615. tagsbox.pack_start(tagb, False, False, 0)
  616. tagsbox.show_all()
  617. # Scroll to the last
  618. def later():
  619. time.sleep(0.1)
  620. def now():
  621. a = tagscrl.get_hadjustment()
  622. a.set_value(a.get_upper())
  623. GLib.idle_add(now)
  624. load_thread = threading.Thread(target=later)
  625. load_thread.start()
  626. # The threading is needed, since we want to wait
  627. # while GTK will update the UI and only then move
  628. # the adjustent. Becuase else, it will move to the
  629. # last previous, not to the last last.
  630. addt = Gtk.Button("+")
  631. addt.set_relief(Gtk.ReliefStyle.NONE)
  632. tagscont.pack_end(addt, False, False, 0)
  633. def on_entry(w):
  634. add_tag(tagentry.get_text())
  635. tagentry.set_text("")
  636. tagentry = Gtk.Entry()
  637. if auto_fill:
  638. liststore = Gtk.ListStore(str)
  639. completion = Gtk.EntryCompletion()
  640. completion.set_model(liststore)
  641. completion.set_text_column(0)
  642. for i in auto_fill:
  643. liststore.append((i,))
  644. tagentry.set_completion(completion)
  645. completion.set_minimum_key_length(0)
  646. completion.complete()
  647. tagentry.connect("activate", on_entry)
  648. addt.connect("clicked", on_entry)
  649. tagscont.pack_end(tagentry, False, False, False)
  650. for tag in data:
  651. add_tag(tag)
  652. if not return_edit_functions:
  653. return tagscont
  654. else:
  655. return tagscont, update, get
  656. collapsable = Gtk.Expander(label=" Authors / Licenses: ")
  657. last_third.pack_end(collapsable, 0,0,5)
  658. term_box = Gtk.VBox()
  659. collapsable.add(term_box)
  660. ################### LICENSES ######################
  661. tools = Gtk.HBox()
  662. term_box.pack_end(tools, 0,0,5)
  663. tools.pack_start(Gtk.Label("Licenses:"), 0,0,5)
  664. licenses_editor, licenses_updator, licenses_get = tags_editor(win, [], True, [])
  665. tools.pack_start(licenses_editor, 1,1,5)
  666. ################### AUTHORS ######################
  667. tools = Gtk.HBox()
  668. term_box.pack_end(tools, 0,0,5)
  669. tools.pack_start(Gtk.Label("Authors:"), 0,0,5)
  670. authors_editor, authors_updator, authors_get = tags_editor(win, [], True, [])
  671. tools.pack_start(authors_editor, 1,1,5)
  672. ################ FORTH RAW #########################
  673. tools = Gtk.HBox()
  674. last_third.pack_end(tools, 0,0,5)
  675. # Folder opener
  676. def open_folder(w):
  677. path = selected_asset.get("path")
  678. if path.endswith(".png"):
  679. path = path[:path.rfind("/")+1]
  680. Popen(["xdg-open", path])
  681. open_folder_b = Gtk.Button("Open Folder")
  682. open_folder_b.connect("clicked", open_folder)
  683. tools.pack_end(open_folder_b, 0,0,5)
  684. # Name of the asset
  685. tools.pack_start(Gtk.Label("Asset:"), 0,0,5)
  686. def on_asset_name(w):
  687. asset = selected_asset.get("path")
  688. image = selected_asset.get("image")
  689. if asset_name.get_text() and image:
  690. win.images[asset]["title"] = asset_name.get_text()
  691. tree = list(treebox.get_children())[0].get_child()
  692. tree_store = tree.get_model()
  693. tree_path = str(tree.get_cursor().path)
  694. tree_path = tree_path[:tree_path.rfind(":")]
  695. tree_iter = tree_store.get_iter(tree_path)
  696. tree_store.set_value(tree_iter, 0, w.get_text())
  697. asset_name = Gtk.Entry()
  698. asset_name.connect("changed", on_asset_name)
  699. tools.pack_start(asset_name, 0,0,5)
  700. def on_color_set(w):
  701. r,g,b,a = w.get_rgba()
  702. r = int(r*255)
  703. g = int(g*255)
  704. b = int(b*255)
  705. def rgb_to_hex(rgb):
  706. return '#%02x%02x%02x' % rgb
  707. value = rgb_to_hex((r,g,b))
  708. asset = selected_asset.get("path")
  709. win.images[asset]["menu_color"] = value
  710. asset_color = Gtk.ColorButton()
  711. asset_color.connect('color-set', on_color_set)
  712. tools.pack_start(asset_color, 0,0,5)
  713. tools.pack_start(Gtk.VSeparator(), 0,0,10)
  714. # ADD NEW ASSET
  715. add_asset = Gtk.Button("+ Asset")
  716. add_asset.connect("clicked", on_add_image, "Asset")
  717. pannel.pack_start(add_asset)
  718. last_third.pack_end(Gtk.HSeparator(), 0,0,10)
  719. ################ THIRD RAW #########################
  720. tools = Gtk.HBox()
  721. last_third.pack_end(tools, 0,0,5)
  722. # The name of the sprite
  723. tools.pack_start(Gtk.Label("Sprite:"), 0,0,5)
  724. def on_sprite_name(w):
  725. asset = selected_asset.get("path")
  726. image = selected_asset.get("image")
  727. try:
  728. for cf, i in enumerate(win.images[asset][image]):
  729. if w.get_text() and image:
  730. win.images[asset][image][cf]["title"] = w.get_text()
  731. # Updating the treeview
  732. tree = list(treebox.get_children())[0].get_child()
  733. tree_store = tree.get_model()
  734. tree_iter = tree_store.get_iter(tree.get_cursor().path)
  735. tree_store.set_value(tree_iter, 0, w.get_text())
  736. except Exception as e:
  737. print(e)
  738. pass
  739. sprite_name = Gtk.Entry()
  740. sprite_name.connect("changed", on_sprite_name)
  741. tools.pack_start(sprite_name, 0,0,5)
  742. add_sprite = Gtk.Button(" + Sprite")
  743. add_sprite.connect("clicked", on_add_image, "Sprite")
  744. tools.pack_start(add_sprite, 0,0,5)
  745. def on_remove_sprite(w):
  746. # First we need to see how much we have
  747. # if less then 2 sprites in the asset ( 1 )
  748. # we are going to abort.
  749. asset = selected_asset.get("path")
  750. image = selected_asset.get("image")
  751. if len(win.images[asset]) > 5: # Including metedata it's 5
  752. del win.images[asset][image]
  753. # Updating the treeview
  754. tree = list(treebox.get_children())[0].get_child()
  755. tree_store = tree.get_model()
  756. tree_iter = tree_store.get_iter(tree.get_cursor().path)
  757. tree_store.remove(tree_iter)
  758. remove_sprite = Gtk.Button(" - Sprite")
  759. remove_sprite.connect("clicked", on_remove_sprite)
  760. tools.pack_start(remove_sprite, 0,0,5)
  761. last_third.pack_end(Gtk.HSeparator(), 0,0,10)
  762. ################ SECOND RAW #########################
  763. tools = Gtk.HBox()
  764. last_third.pack_end(tools, 0,0,5)
  765. add_frame = Gtk.Button(" + Frame")
  766. add_frame.connect("clicked", on_add_image, "Frame")
  767. tools.pack_start(add_frame, 0,0,5)
  768. def on_remove_frame(w):
  769. asset = selected_asset.get("path")
  770. image = selected_asset.get("image")
  771. if selected_frame["is"] != None and len(win.images[asset][image]) > 1:
  772. del win.images[asset][image][selected_frame["is"]]
  773. for i in frames_box.get_children():
  774. i.destroy()
  775. for n, i in enumerate(win.images[asset][image]):
  776. toggle = Gtk.ToggleButton("Frame "+str(n))
  777. toggle.connect("clicked", on_toggle_frame, n)
  778. toggle.set_relief(Gtk.ReliefStyle.NONE)
  779. frames_box.pack_start(toggle,0,0,0)
  780. frames_box.show_all()
  781. selected_frame["is"] = None
  782. remove_frame = Gtk.Button(" - Frame")
  783. remove_frame.connect("clicked", on_remove_frame)
  784. tools.pack_start(remove_frame, 0,0,5)
  785. change_frame = Gtk.Button(" Replace Frame")
  786. change_frame.connect("clicked", on_add_image, "ChangeFrame")
  787. tools.pack_start(change_frame, 0,0,5)
  788. def on_copy_image(w):
  789. asset = selected_asset.get("path")
  790. image = selected_asset.get("image")
  791. clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
  792. f = "/tmp/clipboard_copy.png"
  793. cf = selected_frame["is"]
  794. if cf == None:
  795. cf = 0
  796. canvas = win.images[asset][image][cf]["binary"]
  797. canvas.write_to_png(f)
  798. img = Gtk.Image()
  799. img.set_from_file(f)
  800. clipboard.set_image(img.get_pixbuf())
  801. clipboard.store()
  802. copy_frame = Gtk.Button(" Copy Frame")
  803. copy_frame.connect("clicked", on_copy_image)
  804. tools.pack_start(copy_frame, 0,0,5)
  805. ############### EXTRA MENU ##################
  806. def on_toggle(w, key):
  807. asset = selected_asset.get("path")
  808. image = selected_asset.get("image")
  809. cf = selected_frame["is"]
  810. if cf == None:
  811. for cf, n in enumerate(win.images[asset][image]):
  812. win.images[asset][image][cf][key] = w.get_active()
  813. return
  814. win.images[asset][image][cf][key] = w.get_active()
  815. extra_menu = Gtk.Popover()
  816. extra_button = Gtk.MenuButton("Extra", popover=extra_menu)
  817. tools.pack_start(extra_button, 0,0,5)
  818. tools = Gtk.VBox()
  819. extra_menu.add(tools)
  820. collision_toggle = Gtk.CheckButton("Collision")
  821. collision_toggle.set_active(True)
  822. collision_toggle.connect("clicked", on_toggle, "collision")
  823. tools.pack_start(collision_toggle, 0,0,5)
  824. dynamic_toggle = Gtk.CheckButton("Dynamic")
  825. dynamic_toggle.set_active(False)
  826. dynamic_toggle.connect("clicked", on_toggle, "dynamic")
  827. tools.pack_start(dynamic_toggle, 0,0,5)
  828. main_character_toggle = Gtk.CheckButton("Main Character")
  829. main_character_toggle.set_active(False)
  830. main_character_toggle.connect("clicked", on_toggle, "main_character")
  831. tools.pack_start(main_character_toggle, 0,0,5)
  832. ramp_toggle = Gtk.CheckButton("A Ramp")
  833. ramp_toggle.set_active(False)
  834. ramp_toggle.connect("clicked", on_toggle, "ramp")
  835. tools.pack_start(ramp_toggle, 0,0,5)
  836. invisible_toggle = Gtk.CheckButton("Invisible")
  837. invisible_toggle.set_active(False)
  838. invisible_toggle.connect("clicked", on_toggle, "invisible")
  839. tools.pack_start(invisible_toggle, 0,0,5)
  840. damage_toggle = Gtk.CheckButton("Damage")
  841. damage_toggle.set_active(False)
  842. damage_toggle.connect("clicked", on_toggle, "damage")
  843. tools.pack_start(damage_toggle, 0,0,5)
  844. damage_amount_box = Gtk.HBox()
  845. tools.pack_start(damage_amount_box, 0,0,5)
  846. damage_amount_adjustment = Gtk.Adjustment(0.01,
  847. lower = 0,
  848. upper = 1,
  849. step_increment = 0.001)
  850. damage_amount_spin = Gtk.SpinButton(adjustment = damage_amount_adjustment, digits = 3)
  851. damage_amount_box.pack_start(Gtk.Label("Damage amount: "), 0,0,5)
  852. damage_amount_box.pack_start(damage_amount_spin, 1,1,5)
  853. def on_changed(w):
  854. asset = selected_asset.get("path")
  855. image = selected_asset.get("image")
  856. if selected_frame["is"] != None or len(win.images[asset][image]) == 1:
  857. cf = selected_frame["is"]
  858. if cf == None:
  859. cf = 0
  860. win.images[asset][image][cf]["damage_amount"] = damage_amount_spin.get_value()
  861. damage_amount_spin.connect("value-changed", on_changed)
  862. tools.show_all()
  863. ######################## FIRST RAW ########################
  864. tools = Gtk.HBox()
  865. last_third.pack_end(tools, 0,0,5)
  866. def do_zoom_plus(w):
  867. win.zoom += 1
  868. zoom_plus = Gtk.Button("+")
  869. zoom_plus.connect("clicked", do_zoom_plus)
  870. tools.pack_start(zoom_plus, 0,0,5)
  871. def do_zoom_minus(w):
  872. win.zoom -= 1
  873. if win.zoom < 1:
  874. win.zoom = 1
  875. zoom_minus = Gtk.Button("-")
  876. zoom_minus.connect("clicked", do_zoom_minus)
  877. tools.pack_start(zoom_minus, 0,0,5)
  878. tools.pack_start(Gtk.VSeparator(), 0,0,10)
  879. tools.pack_start(Gtk.Label("Offset x:"), 0,0,1)
  880. xadjust = Gtk.Adjustment(0,
  881. lower=-1000000000,
  882. upper=1000000000,
  883. step_increment=1)
  884. xentry = Gtk.SpinButton(adjustment=xadjust)
  885. tools.pack_start(xentry, 0,0,1)
  886. def on_changed(w):
  887. asset = selected_asset.get("path")
  888. image = selected_asset.get("image")
  889. if selected_frame["is"] != None or len(win.images[asset][image]) == 1:
  890. cf = selected_frame["is"]
  891. if cf == None:
  892. cf = 0
  893. offx = int(xentry.get_value())
  894. offy = int(yentry.get_value())
  895. win.images[asset][image][cf]["offset"] = [int(offx), int(offy)]
  896. xentry.connect("value-changed", on_changed)
  897. tools.pack_start(Gtk.Label("Offset y:"), 0,0,1)
  898. yadjust = Gtk.Adjustment(0,
  899. lower=-1000000000,
  900. upper=1000000000,
  901. step_increment=1)
  902. yentry = Gtk.SpinButton(adjustment=yadjust)
  903. tools.pack_start(yentry, 0,0,1)
  904. yentry.connect("value-changed", on_changed)
  905. win.show_all()
  906. Gtk.main()