studio_nodes.py 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. # GTK module ( Graphical interface
  6. import gi
  7. gi.require_version('Gtk', '3.0')
  8. from gi.repository import Gtk
  9. import cairo
  10. # Own modules
  11. from settings import settings
  12. from settings import talk
  13. from settings import fileformats
  14. from settings import oscalls
  15. # Studio
  16. from studio import story
  17. from studio import analytics
  18. from studio import studio_dialogs
  19. from studio import checklist
  20. # UI modules
  21. from UI import UI_testing
  22. from UI import UI_color
  23. from UI import UI_elements
  24. from UI import UI_math
  25. def node_dot(layer, win, x, y, direction="in", entry="end"):
  26. # This function will draw a dot to which various nodes are connected. And
  27. # from which connections going off.
  28. # This is a very alpha version of the dot thing
  29. raw_entry = entry
  30. if type(entry) == list:
  31. entry = entry[0]+":"+entry[1]
  32. if entry.startswith("scene:") or entry in ["start", "end"]:
  33. UI_color.set(layer, win, "node_script")
  34. elif entry.startswith("blend:"):
  35. UI_color.set(layer, win, "node_blendfile")
  36. else:
  37. UI_color.set(layer, win, "node_imagefile")
  38. UI_elements.roundrect(layer, win,
  39. x,
  40. y,
  41. 0,
  42. 0,
  43. 6)
  44. if int(win.current["mx"]) in range(int(x), int(x+12))\
  45. and int(win.current["my"]) in range(int(y), int(y+12)):
  46. UI_color.set(layer, win, "text_normal")
  47. UI_elements.roundrect(layer, win,
  48. x,
  49. y,
  50. 0,
  51. 0,
  52. 6,
  53. fill=False)
  54. layer.stroke()
  55. win.current["cursor"] = win.cursors["arrow"]
  56. # Start drawing the line
  57. if not win.previous["LMB"] and win.current["LMB"] and direction != "in":
  58. # Out point
  59. win.current["tool"] = "connect"
  60. win.current["draw_dot"] = raw_entry
  61. elif not win.previous["LMB"] and win.current["LMB"] and direction == "in" and win.current["tool"] != "connect":
  62. # Take out of the in point
  63. try:
  64. found = False
  65. win.current["tool"] = "connect"
  66. for arrow in win.story["arrows"]:
  67. if raw_entry == arrow[1]:
  68. win.current["draw_dot"] = arrow[0]#.copy()
  69. found = arrow
  70. break
  71. if found:
  72. win.story["arrows"].remove(found)
  73. else:
  74. win.current["draw_dot"] = "end"
  75. except:
  76. pass
  77. # Refrashing analytics
  78. story.save(win.project, win.story)
  79. win.analytics = analytics.load(win.project)
  80. analytics.save(win.project, win.analytics)
  81. # Connecting the line
  82. if win.current["tool"] == "connect" and direction == "in" and not win.current["LMB"]:
  83. # Connecting the scenes together
  84. if raw_entry[0] not in ["file", "blend"] and win.current["draw_dot"][0] not in ["file", "blend"]:
  85. new_arrow = [
  86. win.current["draw_dot"],
  87. raw_entry
  88. ]
  89. if new_arrow not in win.story["arrows"]:
  90. win.story["arrows"].append(new_arrow)
  91. # Copying images to places
  92. elif raw_entry[0] == "asset" and win.current["draw_dot"][0] == "file":
  93. new = oscalls.copy_file(
  94. win,
  95. win.current["draw_dot"][1],
  96. "dev"+raw_entry[1])
  97. win.story["links"].append([
  98. "file", new, [
  99. win.current["mx"]-win.story["camera"][0]-75,
  100. win.current["my"]-win.story["camera"][1]-75
  101. ],
  102. "" # Parent
  103. ])
  104. # Now let's select and move the thing
  105. win.story["selected"] = [["file", len(win.story["links"])-1]]
  106. win.current["tool"] = "grab"
  107. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  108. elif raw_entry[0] == "blend" and win.current["draw_dot"][0] == "blend":
  109. new = oscalls.copy_file(
  110. win,
  111. win.current["draw_dot"][1],
  112. "dev"+raw_entry[1])
  113. win.story["links"].append([
  114. "file", new, [
  115. win.current["mx"]-win.story["camera"][0]-75,
  116. win.current["my"]-win.story["camera"][1]-75
  117. ],
  118. "" # Parent
  119. ])
  120. # Now let's select and move the thing
  121. win.story["selected"] = [["file", len(win.story["links"])-1]]
  122. win.current["tool"] = "grab"
  123. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  124. UI_math.filter_arrows(win)
  125. # Refrashing analytics
  126. story.save(win.project, win.story)
  127. win.analytics = analytics.load(win.project)
  128. analytics.save(win.project, win.analytics)
  129. if direction != "in":
  130. win.out_dots[entry] = [x+6, y+6]
  131. elif entry.startswith("scene:") or entry in ["start", "end"]:
  132. for arrow in win.story["arrows"]:
  133. if raw_entry == arrow[1]:
  134. fr = arrow[0]
  135. if type(fr) == list:
  136. fr = fr[0]+":"+fr[1]
  137. try:
  138. UI_color.set(layer, win, "node_script")
  139. layer.move_to(
  140. win.out_dots[fr][0],
  141. win.out_dots[fr][1]
  142. )
  143. layer.line_to(x+6, y+6)
  144. layer.stroke()
  145. except:
  146. pass
  147. elif entry.startswith("asset:"):
  148. for link in win.story["links"]:
  149. if link[0] == "file":
  150. link = link[1]
  151. fr = "file:"+link
  152. if entry[entry.find(":")+1:] in link:
  153. try:
  154. UI_color.set(layer, win, "node_imagefile")
  155. layer.move_to(
  156. win.out_dots[fr][0],
  157. win.out_dots[fr][1]
  158. )
  159. layer.line_to(x+6, y+6)
  160. layer.stroke()
  161. except:
  162. pass
  163. elif entry.startswith("blend:"):
  164. for link in win.story["links"]:
  165. if link[0] == "file":
  166. link = link[1]
  167. fr = "blend:"+link
  168. if entry[entry.find(":")+1:]+"/" in link or entry[entry.find(":")+1:]+"." in link:
  169. try:
  170. UI_color.set(layer, win, "node_blendfile")
  171. layer.move_to(
  172. win.out_dots[fr][0],
  173. win.out_dots[fr][1]
  174. )
  175. layer.line_to(x+6, y+6)
  176. layer.stroke()
  177. except:
  178. pass
  179. def start_node(outlayer, win, x, y, width, height):
  180. # This function will draw a start node in the top left corner of the story
  181. # editor. This is where the story begins.
  182. # Making the layer
  183. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  184. layer = cairo.Context(surface)
  185. # Clip
  186. UI_elements.roundrect(layer, win,
  187. 0,
  188. 0,
  189. width,
  190. height,
  191. 10,
  192. fill=False)
  193. layer.clip()
  194. # Background
  195. UI_color.set(layer, win, "dark_overdrop")
  196. layer.rectangle(0,0,width, height)
  197. layer.fill()
  198. # top banner
  199. UI_color.set(layer, win, "node_badfile")
  200. layer.rectangle(0,0,width, 20)
  201. layer.fill()
  202. # Text saying START
  203. UI_color.set(layer, win, "text_normal")
  204. layer.set_font_size(15)
  205. layer.move_to(width/2-len(talk.text("Start"))*9/2,15)
  206. layer.show_text(talk.text("Start"))
  207. # Outputting the layer
  208. outlayer.set_source_surface(surface, x, y)
  209. outlayer.paint()
  210. # Dot
  211. node_dot(outlayer, win, x+width-7, y+height-12, direction="out", entry="start")
  212. def end_node(outlayer, win, x, y, width, height):
  213. # This function will draw a end node in the bottom right corner of the story
  214. # editor. This is where the story ends.
  215. # Making the layer
  216. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  217. layer = cairo.Context(surface)
  218. # Clip
  219. UI_elements.roundrect(layer, win,
  220. 0,
  221. 0,
  222. width,
  223. height,
  224. 10,
  225. fill=False)
  226. layer.clip()
  227. # Background
  228. UI_color.set(layer, win, "dark_overdrop")
  229. layer.rectangle(0,0,width, height)
  230. layer.fill()
  231. # top banner
  232. UI_color.set(layer, win, "node_badfile")
  233. layer.rectangle(0,0,width, 20)
  234. layer.fill()
  235. # Text saying END
  236. UI_color.set(layer, win, "text_normal")
  237. layer.set_font_size(15)
  238. layer.move_to(width/2-len(talk.text("End"))*9/2,15)
  239. layer.show_text(talk.text("End"))
  240. # Outputting the layer
  241. outlayer.set_source_surface(surface, x, y)
  242. outlayer.paint()
  243. # Dot
  244. node_dot(outlayer, win, x-2, y+height-12)
  245. def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0):
  246. # This function will draw scene nodes.
  247. entry = ['scene', name]
  248. width = max(width,20)
  249. height = max(height,20)
  250. doparent = True
  251. if int(x) in range(int(0-width), int(win.current["w"]))\
  252. and int(y) in range(int(0-height), int(win.current["h"])):
  253. # Making the layer
  254. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  255. layer = cairo.Context(surface)
  256. selected = False
  257. if win.url == "story_editor"\
  258. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  259. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  260. if win.current["LMB"] and entry in win.story["selected"]:
  261. if int(win.current["LMB"][0]) in range(int(x), int(x+width))\
  262. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  263. and win.current["tool"] == "selection":
  264. win.current["tool"] = "grab"
  265. win.story["active"] = entry
  266. # Launching the item
  267. if int(win.current["mx"]) in range(int(x), int(x+width))\
  268. and int(win.current["my"]) in range(int(y), int(y+height)):
  269. win.current["cursor"] = win.cursors["hand"]
  270. if not win.current["LMB"] and win.previous["LMB"]\
  271. and entry in win.story["selected"]\
  272. and int(win.current["mx"]) == int(win.previous["LMB"][0])\
  273. and int(win.current["my"]) == int(win.previous["LMB"][1]):
  274. win.url = "script"
  275. win.cur = "/"+name
  276. win.current["tool"] = "selection"
  277. if win.current["LMB"] and win.current["tool"] == "selection":
  278. # If mouse over. But way more complex. Because we might select more then
  279. # 1 scene at ones.
  280. mx = win.current["mx"]
  281. my = win.current["my"]
  282. pmx = win.current["LMB"][0]
  283. pmy = win.current["LMB"][1]
  284. intersect = UI_math.rectangle_overlap(
  285. [mx, my, pmx-mx, pmy-my],
  286. [x,y,width, height])
  287. # Now let's make a higlight
  288. if intersect:
  289. selected = True
  290. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  291. # If you released the mouse while selecting. Then do selecting. I guess.
  292. mx = win.previous["mx"]
  293. my = win.previous["my"]
  294. pmx = win.previous["LMB"][0]
  295. pmy = win.previous["LMB"][1]
  296. intersect = UI_math.rectangle_overlap(
  297. [mx, my, pmx-mx, pmy-my],
  298. [x,y,width, height])
  299. # Now let's make a selection
  300. if intersect:
  301. if entry not in win.story["selected"]:
  302. win.story["selected"].append(entry)
  303. if win.story["active"] not in win.story["selected"]:
  304. win.story["active"] = entry
  305. if entry in win.story["selected"]:
  306. selected = True
  307. if selected:
  308. if win.current["tool"] == "selection":
  309. UI_math.rectangle_surround(win, "selection",
  310. win.szone[0], win.szone[1],
  311. [x,y], [width, height]
  312. )
  313. # Now let's make the stratching thing do it's stratching. It's the
  314. # circle in the bottom, right corner of the selection.
  315. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  316. # let's get the points of the selection zone.
  317. zx = win.szone[0][0]
  318. zy = win.szone[0][1]
  319. zsx = win.previous["LMB"][0]
  320. zsy = win.previous["LMB"][1]
  321. if win.current["mx"] > zx and win.current["my"] > zy:
  322. # now let's get the motion fraction.
  323. mvx = (x - zx) / (zsx - zx)
  324. mvy = (y - zy) / (zsy - zy)
  325. svx = (win.current["mx"]- zx) / (win.previous["mx"]-zx)
  326. svy = (win.current["my"]- zy) / (win.previous["my"]-zy)
  327. # now let's apply these
  328. win.story["scenes"][name]["position"][0]\
  329. += (win.current["mx"] - win.previous["mx"])*mvx
  330. win.story["scenes"][name]["position"][1]\
  331. += (win.current["my"] - win.previous["my"])*mvy
  332. win.story["scenes"][name]["size"][0]\
  333. = max(win.story["scenes"][name]["size"][0] * svx, 40)
  334. win.story["scenes"][name]["size"][1]\
  335. = max(win.story["scenes"][name]["size"][1] * svy, 40)
  336. # Now let's do the simple grab tool.
  337. if win.current["tool"] == "grab":
  338. # If you press shift, You take things out of events
  339. if 65505 in win.current["keys"]:
  340. doparent = False
  341. try:
  342. if win.current["LMB"]:
  343. x += win.current["mx"] - win.current["LMB"][0]
  344. y += win.current["my"] - win.current["LMB"][1]
  345. elif win.previous["LMB"]:
  346. win.story["scenes"][name]["position"][0]\
  347. += win.previous["mx"] - win.previous["LMB"][0]
  348. win.story["scenes"][name]["position"][1]\
  349. += win.previous["my"] - win.previous["LMB"][1]
  350. x += win.previous["mx"] - win.previous["LMB"][0]
  351. y += win.previous["my"] - win.previous["LMB"][1]
  352. # Parent removing / assigning
  353. if not doparent:
  354. win.story["scenes"][name]["parent"] = ""
  355. elif not win.story["scenes"][name]["parent"]:
  356. for event in win.story["events"]:
  357. mx, my = win.story["events"][event]["position"]
  358. pmx, pmy = win.story["events"][event]["size"]
  359. if UI_math.rectangle_overlap(
  360. [mx, my, pmx, pmy],
  361. [x,y,width, height]):
  362. win.story["scenes"][name]["parent"] = event
  363. break
  364. elif win.current["tool"] != "connect":
  365. win.current["tool"] = "selection"
  366. except:
  367. raise()
  368. # The selected outline
  369. UI_color.set(outlayer, win, "progress_background")
  370. if win.story["active"] == entry:
  371. UI_color.set(outlayer, win, "text_normal")
  372. outlayer.set_line_width(4)
  373. UI_elements.roundrect(outlayer, win,
  374. x,
  375. y,
  376. width,
  377. height,
  378. 10,
  379. fill=False)
  380. outlayer.stroke()
  381. outlayer.set_line_width(2)
  382. # Clip
  383. UI_elements.roundrect(layer, win,
  384. 0,
  385. 0,
  386. width,
  387. height,
  388. 10,
  389. fill=False)
  390. layer.clip()
  391. # Tooltip
  392. if int(win.current["mx"]) in range(int(x), int(x+width))\
  393. and int(win.current["my"]) in range(int(y), int(y+height)):
  394. UI_elements.tooltip(win, name)
  395. # Background
  396. UI_color.set(layer, win, "dark_overdrop")
  397. layer.rectangle(0,0,width, height)
  398. layer.fill()
  399. # top banner
  400. UI_color.set(layer, win, "node_script")
  401. layer.rectangle(0,0,width, 20)
  402. layer.fill()
  403. # Text saying The name of the scene
  404. UI_color.set(layer, win, "text_normal")
  405. layer.set_font_size(15)
  406. layer.move_to(15,15)
  407. layer.show_text(name)
  408. # Fraction
  409. UI_color.set(layer, win, "progress_background")
  410. UI_elements.roundrect(layer, win,
  411. 10,
  412. height-20,
  413. width-20,
  414. 0,
  415. 5)
  416. UI_color.set(layer, win, "progress_active")
  417. UI_elements.roundrect(layer, win,
  418. 10,
  419. height-20,
  420. (width-20)*fraction,
  421. 0,
  422. 5)
  423. # Outputting the layer
  424. outlayer.set_source_surface(surface, x, y)
  425. outlayer.paint()
  426. # In case there is a parent event in the scene.
  427. if win.story["scenes"][name]["parent"] and doparent:
  428. parent = win.story["scenes"][name]["parent"]
  429. UI_math.rectangle_surround(win, parent,
  430. win.story["events"][parent]["position"],
  431. win.story["events"][parent]["size"],
  432. [x,y], [width, height]
  433. )
  434. # Dots
  435. node_dot(outlayer, win, x-5, y+25, entry=entry)
  436. node_dot(outlayer, win, x+width-7, y+25, direction="out",entry=entry)
  437. def event_node(outlayer, win, x, y, width, height, name="Unknown"):
  438. # This function draws events.
  439. x = x - 20
  440. y = y - 20
  441. width = width + 40
  442. height = height + 40
  443. entry = ['event', name]
  444. # Making sure the size is alright
  445. if width < 100:
  446. width = 100
  447. if height < 100:
  448. height = 100
  449. # Making the layer
  450. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  451. layer = cairo.Context(surface)
  452. # Clip
  453. UI_elements.roundrect(layer, win,
  454. 0,
  455. 0,
  456. width,
  457. height,
  458. 10,
  459. fill=False)
  460. layer.clip()
  461. # Background
  462. UI_color.set(layer, win, "dark_overdrop")
  463. layer.rectangle(0,0,width, height)
  464. layer.fill()
  465. # Here should be the event name thing. But I thought that it wasn't
  466. # important for a simple framing thing.
  467. # Outputting the layer
  468. outlayer.set_source_surface(surface, x, y)
  469. outlayer.paint()
  470. def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linktype="file"):
  471. filetype = ""
  472. # This node will output links to files.
  473. if linktype == "asset":
  474. width += 50
  475. height += 80
  476. else:
  477. # Let's find out what type of file is it.
  478. for f in fileformats.images:
  479. if name.endswith(f):
  480. filetype = "image"
  481. if not filetype:
  482. for f in fileformats.videos:
  483. if name.endswith(f):
  484. filetype = "video"
  485. doparent = True
  486. if int(x) in range(int(0-width), int(win.current["w"]))\
  487. and int(y) in range(int(0-height), int(win.current["h"])):
  488. # Making the layer
  489. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  490. layer = cairo.Context(surface)
  491. entry = [linktype, num]
  492. selected = False
  493. if win.url == "story_editor"\
  494. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  495. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  496. if win.current["LMB"] and entry in win.story["selected"]:
  497. if int(win.current["LMB"][0]) in range(int(x), int(x+width))\
  498. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  499. and win.current["tool"] == "selection":
  500. win.current["tool"] = "grab"
  501. win.story["active"] = entry
  502. # Launching the item
  503. if int(win.current["mx"]) in range(int(x), int(x+width))\
  504. and int(win.current["my"]) in range(int(y), int(y+height)):
  505. win.current["cursor"] = win.cursors["hand"]
  506. if not win.current["LMB"] and win.previous["LMB"]\
  507. and entry in win.story["selected"]\
  508. and int(win.current["mx"]) == int(win.previous["LMB"][0])\
  509. and int(win.current["my"]) == int(win.previous["LMB"][1]):
  510. if linktype == "file":
  511. oscalls.file_open(win, name)
  512. elif int(win.current["mx"]) > x+40: # This is a hack. But whatever.
  513. win.url = "assets"
  514. win.cur = name
  515. win.current["tool"] = "selection"
  516. win.previous["LMB"] = False
  517. if win.current["LMB"] and win.current["tool"] == "selection":
  518. # If mouse over. But way more complex. Because we might select more then
  519. # 1 scene at ones.
  520. mx = win.current["mx"]
  521. my = win.current["my"]
  522. pmx = win.current["LMB"][0]
  523. pmy = win.current["LMB"][1]
  524. intersect = UI_math.rectangle_overlap(
  525. [mx, my, pmx-mx, pmy-my],
  526. [x,y,width, height])
  527. # Now let's make a higlight
  528. if intersect:
  529. selected = True
  530. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  531. # If you released the mouse while selecting. Then do selecting. I guess.
  532. mx = win.previous["mx"]
  533. my = win.previous["my"]
  534. pmx = win.previous["LMB"][0]
  535. pmy = win.previous["LMB"][1]
  536. intersect = UI_math.rectangle_overlap(
  537. [mx, my, pmx-mx, pmy-my],
  538. [x,y,width, height])
  539. # Now let's make a selection
  540. if intersect:
  541. if entry not in win.story["selected"]:
  542. win.story["selected"].append(entry)
  543. # Making it active.
  544. if win.story["active"] not in win.story["selected"]:
  545. win.story["active"] = entry
  546. if entry in win.story["selected"]:
  547. selected = True
  548. if selected:
  549. if win.current["tool"] == "selection":
  550. UI_math.rectangle_surround(win, "selection",
  551. win.szone[0], win.szone[1],
  552. [x,y], [width, height]
  553. )
  554. # Now let's make the stratching thing do it's stratching. It's the
  555. # circle in the bottom, right corner of the selection.
  556. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  557. # let's get the points of the selection zone.
  558. zx = win.szone[0][0]
  559. zy = win.szone[0][1]
  560. zsx = win.previous["LMB"][0]
  561. zsy = win.previous["LMB"][1]
  562. if win.current["mx"] > zx and win.current["my"] > zy:
  563. # now let's get the motion fraction.
  564. mvx = (x - zx) / (zsx - zx)
  565. mvy = (y - zy) / (zsy - zy)
  566. # now let's apply these
  567. win.story["links"][num][2][0]\
  568. += (win.current["mx"] - win.previous["mx"])*mvx
  569. win.story["links"][num][2][1]\
  570. += (win.current["my"] - win.previous["my"])*mvy
  571. if win.current["tool"] == "grab":
  572. # If you press shift, You take things out of events
  573. if 65505 in win.current["keys"]:
  574. doparent = False
  575. try:
  576. if win.current["LMB"]:
  577. x += win.current["mx"] - win.current["LMB"][0]
  578. y += win.current["my"] - win.current["LMB"][1]
  579. elif win.previous["LMB"]:
  580. win.story["links"][num][2][0]\
  581. += win.previous["mx"] - win.previous["LMB"][0]
  582. win.story["links"][num][2][1]\
  583. += win.previous["my"] - win.previous["LMB"][1]
  584. x += win.previous["mx"] - win.previous["LMB"][0]
  585. y += win.previous["my"] - win.previous["LMB"][1]
  586. # Parent removing / assigning
  587. if not doparent:
  588. win.story["links"][num][3] = ""
  589. elif not win.story["links"][num][3]:
  590. for event in win.story["events"]:
  591. mx, my = win.story["events"][event]["position"]
  592. pmx, pmy = win.story["events"][event]["size"]
  593. if UI_math.rectangle_overlap(
  594. [mx, my, pmx, pmy],
  595. [x,y,width, height]):
  596. win.story["links"][num][3] = event
  597. break
  598. elif win.current["tool"] != "connect":
  599. win.current["tool"] = "selection"
  600. except:
  601. raise()
  602. # The selected outline
  603. UI_color.set(outlayer, win, "progress_background")
  604. if win.story["active"] == entry:
  605. UI_color.set(outlayer, win, "text_normal")
  606. outlayer.set_line_width(4)
  607. UI_elements.roundrect(outlayer, win,
  608. x,
  609. y,
  610. width,
  611. height,
  612. 10,
  613. fill=False)
  614. outlayer.stroke()
  615. outlayer.set_line_width(2)
  616. # Clip
  617. UI_elements.roundrect(layer, win,
  618. 0,
  619. 0,
  620. width,
  621. height,
  622. 10,
  623. fill=False)
  624. layer.clip()
  625. # Background
  626. UI_color.set(layer, win, "dark_overdrop")
  627. layer.rectangle(0,0,width, height)
  628. layer.fill()
  629. if linktype == "asset":
  630. assettype = name[:name.rfind("/")].replace("/", "")
  631. if os.path.exists(win.project+"/dev"+name+"/renders/Preview.png"):
  632. UI_elements.image(layer, win,
  633. win.project+"/dev"+name+"/renders/Preview.png",
  634. 0, 0, width, height, cell="story_asset_previews")
  635. elif os.path.exists(win.project+"/dev"+name+"/renders/Preview.jpg"):
  636. UI_elements.image(layer, win,
  637. win.project+"/dev"+name+"/renders/Preview.jpg",
  638. 0, 0, width, height, cell="story_asset_previews")
  639. else:
  640. UI_elements.image(layer, win,
  641. "settings/themes/"+win.settings["Theme"]+"/icons/"+assettype+".png",
  642. width/2-20, height/2-20, 40, 40)
  643. # Here I want to add some buttons for accessing the various folder of
  644. # the asset. And also to link stuff into those folders using nodes.
  645. if int(win.current["mx"]) in range(int(x), int(x+width))\
  646. and int(win.current["my"]) in range(int(y), int(y+height)):
  647. # Darkening thing.
  648. UI_color.set(layer, win, "dark_overdrop")
  649. layer.rectangle(0,0,50, height)
  650. layer.fill()
  651. def after(win, var):
  652. if var:
  653. win.story["links"].append([
  654. "file", var, [
  655. win.current["mx"]-win.story["camera"][0]-75,
  656. win.current["my"]-win.story["camera"][1]-75
  657. ],
  658. ""
  659. ])
  660. # Now let's select and move the thing
  661. win.story["selected"] = [["file", len(win.story["links"])-1]]
  662. win.current["tool"] = "grab"
  663. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  664. # Blendfiles
  665. def do():
  666. studio_dialogs.file_select(win, name+"_blends", after, force=True,
  667. IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=True, VEH=True,
  668. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name)
  669. UI_elements.roundrect(layer, win,
  670. 5,
  671. 25,
  672. 40,
  673. 40,
  674. 10,
  675. do,
  676. "blender",
  677. talk.text("blend_files_folder"),
  678. url="story_editor",
  679. offset=[x,y])
  680. # References
  681. def do():
  682. studio_dialogs.file_select(win, name+"_reference", after, force=True,
  683. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  684. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/reference")
  685. UI_elements.roundrect(layer, win,
  686. 5,
  687. 75,
  688. 40,
  689. 40,
  690. 10,
  691. do,
  692. "idea",
  693. talk.text("reference_folder"),
  694. url="story_editor",
  695. offset=[x,y])
  696. # Textures
  697. def do():
  698. studio_dialogs.file_select(win, name+"_textures", after, force=True,
  699. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  700. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/tex")
  701. UI_elements.roundrect(layer, win,
  702. 5,
  703. 125,
  704. 40,
  705. 40,
  706. 10,
  707. do,
  708. "texture",
  709. talk.text("tex_folder"),
  710. url="story_editor",
  711. offset=[x,y])
  712. # Renders
  713. def do():
  714. studio_dialogs.file_select(win, name+"_renders", after, force=True,
  715. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  716. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/renders")
  717. UI_elements.roundrect(layer, win,
  718. 5,
  719. 175,
  720. 40,
  721. 40,
  722. 10,
  723. do,
  724. "render",
  725. talk.text("renders_folder"),
  726. url="story_editor",
  727. offset=[x,y])
  728. # Fraction
  729. fraction = story.get_asset_data(win, name)["fraction"]
  730. UI_color.set(layer, win, "progress_background")
  731. UI_elements.roundrect(layer, win,
  732. 65,
  733. height-20,
  734. width-85,
  735. 0,
  736. 5)
  737. UI_color.set(layer, win, "progress_active")
  738. UI_elements.roundrect(layer, win,
  739. 65,
  740. height-20,
  741. (width-85)*fraction,
  742. 0,
  743. 5)
  744. else:
  745. # Fraction
  746. fraction = story.get_asset_data(win, name)["fraction"]
  747. UI_color.set(layer, win, "progress_background")
  748. UI_elements.roundrect(layer, win,
  749. 10,
  750. height-20,
  751. width-20,
  752. 0,
  753. 5)
  754. UI_color.set(layer, win, "progress_active")
  755. UI_elements.roundrect(layer, win,
  756. 10,
  757. height-20,
  758. (width-20)*fraction,
  759. 0,
  760. 5)
  761. UI_color.set(layer, win, "node_asset")
  762. # Tooltip
  763. if int(win.current["mx"]) in range(int(x+40), int(x+width))\
  764. and int(win.current["my"]) in range(int(y), int(y+height)):
  765. UI_elements.tooltip(win, name)
  766. else:
  767. if os.path.exists(win.project+"/"+name):
  768. UI_elements.image(layer, win,
  769. win.project+"/"+name,
  770. 0, 0, width, height)
  771. else:
  772. UI_elements.image(layer, win,
  773. name,
  774. 0, 0, width, height)
  775. if name.endswith(".progress"):
  776. fraction = checklist.get_fraction(win, name)
  777. UI_color.set(layer, win, "progress_background")
  778. UI_elements.roundrect(layer, win,
  779. 10,
  780. height-20,
  781. width-20,
  782. 0,
  783. 5)
  784. UI_color.set(layer, win, "progress_active")
  785. UI_elements.roundrect(layer, win,
  786. 10,
  787. height-20,
  788. (width-20)*fraction,
  789. 0,
  790. 5)
  791. if name.endswith(".blend"):
  792. UI_color.set(layer, win, "node_blendfile")
  793. if "ast/" in name:
  794. UI_color.set(layer, win, "node_asset")
  795. elif filetype == "image":
  796. UI_color.set(layer, win, "node_imagefile")
  797. elif filetype == "video":
  798. UI_color.set(layer, win, "node_videofile")
  799. else:
  800. UI_color.set(layer, win, "node_script")
  801. # Tooltip
  802. if int(win.current["mx"]) in range(int(x), int(x+width))\
  803. and int(win.current["my"]) in range(int(y), int(y+height)):
  804. UI_elements.tooltip(win, name)
  805. # top banner
  806. layer.rectangle(0,0,width, 20)
  807. layer.fill()
  808. # Text saying The name of the event
  809. UI_color.set(layer, win, "text_normal")
  810. layer.set_font_size(15)
  811. layer.move_to(15,15)
  812. layer.show_text(name[name.rfind("/")+1:])
  813. # Outputting the layer
  814. outlayer.set_source_surface(surface, x, y)
  815. outlayer.paint()
  816. if linktype == "asset":
  817. node_dot(outlayer, win, x-6, y+25+14, entry=["blend", name])
  818. node_dot(outlayer, win, x-6, y+75+14, entry=["asset", name+"/reference"])
  819. node_dot(outlayer, win, x-6, y+125+14, entry=["asset", name+"/tex"])
  820. node_dot(outlayer, win, x-6, y+175+14, entry=["asset", name+"/renders"])
  821. else:
  822. if name.endswith(".blend"):
  823. node_dot(outlayer, win, x+width-6, y+120, entry=["blend", name], direction="out")
  824. elif filetype in ["image", "video"]:
  825. node_dot(outlayer, win, x+width-6, y+120, entry=["file", name], direction="out")
  826. else: # If not in the frame
  827. if linktype == "file":
  828. try:
  829. if name.endswith(".blend"):
  830. del win.out_dots["blend:"+name]
  831. else:
  832. del win.out_dots["file:"+name]
  833. except:
  834. pass
  835. # In case there is a parent event in the scene.
  836. if win.story["links"][num][3] and doparent:
  837. parent = win.story["links"][num][3]
  838. UI_math.rectangle_surround(win, parent,
  839. win.story["events"][parent]["position"],
  840. win.story["events"][parent]["size"],
  841. [x,y], [width, height]
  842. )
  843. def marker(outlayer, win, name ,x, y ):
  844. # This function will draw markers to the screen. They are like shortcuts in
  845. # a story editor space.
  846. if int(x) in range(int(60), int(win.current["w"]-100))\
  847. and int(y) in range(int(60), int(win.current["h"]-80)):
  848. width = 200
  849. height = 40
  850. inscreen = True
  851. else:
  852. x = min(max(x, 60), win.current["w"]-100)
  853. y = min(max(y, 60), win.current["h"]-80)
  854. width = 40
  855. height = 40
  856. inscreen = False
  857. # Making the layer
  858. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  859. layer = cairo.Context(surface)
  860. entry = ["marker", name]
  861. if inscreen:
  862. doparent = True
  863. selected = False
  864. if win.url == "story_editor"\
  865. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  866. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  867. if win.current["LMB"] and entry in win.story["selected"]:
  868. if int(win.current["LMB"][0]) in range(int(x), int(x+40))\
  869. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  870. and win.current["tool"] == "selection":
  871. win.current["tool"] = "grab"
  872. win.story["active"] = entry
  873. if win.current["LMB"] and win.current["tool"] == "selection":
  874. # If mouse over. But way more complex. Because we might select more then
  875. # 1 scene at ones.
  876. mx = win.current["mx"]
  877. my = win.current["my"]
  878. pmx = win.current["LMB"][0]
  879. pmy = win.current["LMB"][1]
  880. intersect = UI_math.rectangle_overlap(
  881. [mx, my, pmx-mx, pmy-my],
  882. [x,y,width, height])
  883. # Now let's make a higlight
  884. if intersect:
  885. selected = True
  886. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  887. # If you released the mouse while selecting. Then do selecting. I guess.
  888. mx = win.previous["mx"]
  889. my = win.previous["my"]
  890. pmx = win.previous["LMB"][0]
  891. pmy = win.previous["LMB"][1]
  892. intersect = UI_math.rectangle_overlap(
  893. [mx, my, pmx-mx, pmy-my],
  894. [x,y,width, height])
  895. # Now let's make a selection
  896. if intersect:
  897. if entry not in win.story["selected"]:
  898. win.story["selected"].append(entry)
  899. # Making it active.
  900. if win.story["active"] not in win.story["selected"]:
  901. win.story["active"] = entry
  902. if entry in win.story["selected"]:
  903. selected = True
  904. if selected:
  905. if win.current["tool"] == "selection":
  906. UI_math.rectangle_surround(win, "selection",
  907. win.szone[0], win.szone[1],
  908. [x,y], [width, height]
  909. )
  910. # Now let's make the stratching thing do it's stratching. It's the
  911. # circle in the bottom, right corner of the selection.
  912. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  913. # let's get the points of the selection zone.
  914. zx = win.szone[0][0]
  915. zy = win.szone[0][1]
  916. zsx = win.previous["LMB"][0]
  917. zsy = win.previous["LMB"][1]
  918. if win.current["mx"] > zx and win.current["my"] > zy:
  919. # now let's get the motion fraction.
  920. mvx = (x - zx) / (zsx - zx)
  921. mvy = (y - zy) / (zsy - zy)
  922. # now let's apply these
  923. win.story["markers"][name][0]\
  924. += (win.current["mx"] - win.previous["mx"])*mvx
  925. win.story["markers"][name][1]\
  926. += (win.current["my"] - win.previous["my"])*mvy
  927. if win.current["tool"] == "grab":
  928. # If you press shift, You take things out of events
  929. if 65505 in win.current["keys"]:
  930. doparent = False
  931. try:
  932. if win.current["LMB"]:
  933. x += win.current["mx"] - win.current["LMB"][0]
  934. y += win.current["my"] - win.current["LMB"][1]
  935. elif win.previous["LMB"]:
  936. win.story["markers"][name][0]\
  937. += win.previous["mx"] - win.previous["LMB"][0]
  938. win.story["markers"][name][1]\
  939. += win.previous["my"] - win.previous["LMB"][1]
  940. x += win.previous["mx"] - win.previous["LMB"][0]
  941. y += win.previous["my"] - win.previous["LMB"][1]
  942. # Parent removing / assigning
  943. if not doparent:
  944. win.story["markers"][name][2] = ""
  945. elif not win.story["markers"][name][2] :
  946. for event in win.story["events"]:
  947. mx, my = win.story["events"][event]["position"]
  948. pmx, pmy = win.story["events"][event]["size"]
  949. if UI_math.rectangle_overlap(
  950. [mx, my, pmx, pmy],
  951. [x,y,width, height]):
  952. win.story["markers"][name][2] = event
  953. print(event)
  954. break
  955. elif win.current["tool"] != "connect":
  956. win.current["tool"] = "selection"
  957. except:
  958. raise()
  959. # The selected outline
  960. UI_color.set(outlayer, win, "progress_background")
  961. if win.story["active"] == entry:
  962. UI_color.set(outlayer, win, "text_normal")
  963. outlayer.set_line_width(4)
  964. UI_elements.roundrect(outlayer, win,
  965. x,
  966. y,
  967. width,
  968. height,
  969. 10,
  970. fill=False)
  971. outlayer.stroke()
  972. outlayer.set_line_width(2)
  973. # Clip
  974. UI_elements.roundrect(layer, win,
  975. 0,
  976. 0,
  977. width,
  978. height,
  979. 10,
  980. fill=False)
  981. layer.clip()
  982. # Background
  983. UI_color.set(layer, win, "dark_overdrop")
  984. layer.rectangle(0,0,width, height)
  985. layer.fill()
  986. # Pin Icon
  987. UI_elements.image(layer, win,
  988. "settings/themes/"+win.settings["Theme"]+"/icons/pin.png",
  989. 0, 0, 40, 40)
  990. # Outputting the layer
  991. outlayer.set_source_surface(surface, x, y)
  992. outlayer.paint()
  993. if inscreen:
  994. # Text editor for the marker's name.
  995. UI_elements.text(outlayer, win, name+"_marker",
  996. x+40,
  997. y,
  998. width-40,
  999. 40,
  1000. set_text=name)
  1001. if name != win.text[name+"_marker"]["text"] and win.text[name+"_marker"]["text"]:
  1002. def do():
  1003. win.story["selected"] = []
  1004. win.story["markers"][win.text[name+"_marker"]["text"]] = [
  1005. x - win.story["camera"][0],
  1006. y - win.story["camera"][1],
  1007. win.story["markers"][name][2]
  1008. ]
  1009. win.textactive = ""
  1010. del win.story["markers"][name]
  1011. UI_elements.roundrect(outlayer, win,
  1012. x+width-40,
  1013. y,
  1014. 40,
  1015. 40,
  1016. 10,
  1017. button=do,
  1018. icon="ok",
  1019. tip=talk.text("checked"))
  1020. if 65293 in win.current["keys"]:
  1021. do()
  1022. # In case there is a parent event in the scene.
  1023. if win.story["markers"][name][2] and doparent:
  1024. parent = win.story["markers"][name][2]
  1025. UI_math.rectangle_surround(win, parent,
  1026. win.story["events"][parent]["position"],
  1027. win.story["events"][parent]["size"],
  1028. [x,y], [width, height]
  1029. )
  1030. else:
  1031. if win.textactive == name+"_marker":
  1032. win.textactive = ""
  1033. def do():
  1034. nex = 0-win.story["markers"][name][0] + win.current["w"]/2
  1035. ney = 0-win.story["markers"][name][1] + win.current["h"]/2
  1036. UI_elements.animate("cameraX", win, win.story["camera"][0],nex, time=20, force=True)
  1037. UI_elements.animate("cameraY", win, win.story["camera"][1],ney, time=20, force=True)
  1038. UI_elements.roundrect(outlayer, win,
  1039. x+width-40,
  1040. y,
  1041. 40,
  1042. 40,
  1043. 10,
  1044. button=do,
  1045. tip=name,
  1046. fill=False)
  1047. outlayer.stroke()
  1048. def user(outlayer, win, name ,x, y, user):
  1049. # This function will draw users from multiuser to the screen.
  1050. if int(x) in range(int(60), int(win.current["w"]-100))\
  1051. and int(y) in range(int(60), int(win.current["h"]-80)):
  1052. width = 200
  1053. height = 40
  1054. inscreen = True
  1055. else:
  1056. x = min(max(x, 60), win.current["w"]-100)
  1057. y = min(max(y, 60), win.current["h"]-80)
  1058. width = 40
  1059. height = 40
  1060. inscreen = False
  1061. # Making the layer
  1062. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  1063. layer = cairo.Context(surface)
  1064. # Clip
  1065. UI_elements.roundrect(layer, win,
  1066. 0,
  1067. 0,
  1068. width,
  1069. height,
  1070. 10,
  1071. fill=False)
  1072. layer.clip()
  1073. # Background
  1074. UI_color.set(layer, win, "dark_overdrop")
  1075. layer.rectangle(0,0,width, height)
  1076. layer.fill()
  1077. # Pin Icon
  1078. UI_elements.image(layer, win,
  1079. "settings/themes/"+win.settings["Theme"]+"/icons/user.png",
  1080. 0, 0, 40, 40)
  1081. # Outputting the layer
  1082. outlayer.set_source_surface(surface, x, y)
  1083. outlayer.paint()
  1084. if inscreen:
  1085. # Text editor for the marker's name.
  1086. UI_elements.text(outlayer, win, name+"_user",
  1087. x+40,
  1088. y,
  1089. width-40,
  1090. 40,
  1091. set_text=name,
  1092. editable=False)
  1093. else:
  1094. # Going to the other user with 1 click
  1095. def do():
  1096. nex = win.multiuser["users"][user]["camera"][0]
  1097. ney = win.multiuser["users"][user]["camera"][1]
  1098. UI_elements.animate("cameraX", win, win.story["camera"][0],nex, time=20, force=True)
  1099. UI_elements.animate("cameraY", win, win.story["camera"][1],ney, time=20, force=True)
  1100. UI_elements.roundrect(outlayer, win,
  1101. x+width-40,
  1102. y,
  1103. 40,
  1104. 40,
  1105. 10,
  1106. button=do,
  1107. tip=name,
  1108. fill=False)
  1109. outlayer.stroke()