Blender-Pipeline/studio/studio_scriptLayer.py

3236 lines
130 KiB
Python

# THIS FILE IS A PART OF VCStudio
# PYTHON 3
import os
import datetime
import re
import json
# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo
# Own modules
from settings import settings
from settings import talk
from settings import fileformats
from settings import oscalls
from project_manager import pm_project
#UI modules
from UI import UI_elements
from UI import UI_color
from UI import UI_math
# story
from studio import story
from studio import checklist
from studio import analytics
from studio import studio_dialogs
from studio import schedule
from studio import history
def layer(win):
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
win.current['h'])
layer = cairo.Context(surface)
#text setting
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
0,
0,
win.current["w"],
win.current["h"],
)
layer.fill()
############################################################################
# This is a file that will draw the text editor of VCStudio. The idea is that
# you not only work on making the movie in VCStudio, but the script to the
# movie is also written in VCStudio. This was already implemented in Blender-
# Organizer legacy. And already implemented partially in VCStudio. As there
# is story editor in place where you can add empty scenes. Or deal with
# scenes that are converted from legacy.
# But as you may guess by the time I'm writting it there is no way of editing
# those scene's contents. Or read them in full. ( You can see peaces in the
# assets )
# The implementation of the editor will be different from Blender-Organizer's
# where I used an off the shelf GTK text writting widget.
# The problem was that I could not really do much with that widget appart from
# text. Don't get me wrong it was good for text. But I could not make for
# example custom ways of drawing particular parts. Like what if I want a
# round-rectangle arround each frase that the crachater's say. Or what if I
# want to draw some custom UI buttons somewhere in the text. Stuff like this.
# Which resulted in a weird workflow. I had a pretty script preview thing.
# and another non-pretty script preview thing. And a script writer thing where
# you type in code to mark thing. Very non pretty. Non user-friendly. And
# you had to learn the code. And stuff. Yeah...
# So here I want to make more of traditional editor. Where you see what you are
# editing as you editing it. Hopefully it's going to work.
############################################################################
####### TOP PANEL #########
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
win.current["w"]/4,
10,
win.current["w"]/2,
50,
10)
# Let's add the toolbar. There is not a lot of tools we need. Since it's
# a program only for writting a script.
# Mark Shot
# Mark Asset
# Start Dialogue
# Link Image
# MARK SHOT
def do():
win.current["key_letter"] = chr(19)
UI_elements.roundrect(layer, win,
win.current["w"]/4+5,
15,
40,
40,
10,
do,
"shot_new",
talk.text("add_shot_tooltip")+"\n\n[Ctrl - S]")
# MARK ASSET
def do():
win.current["key_letter"] = chr(12)
UI_elements.roundrect(layer, win,
win.current["w"]/4+55,
15,
40,
40,
10,
do,
"obj_link",
talk.text("add_asset_tooltip")+"\n\n[Ctrl - L]")
# Do DIALOGUE
def do():
# Later on I'm doing the Ctrl - D combination to add a frase part
# and this gives me a character of 00000011 or in other words a 4.
# So i guess making it thing that I pressed Ctrl-D is fine.
win.current["key_letter"] = chr(4)
UI_elements.roundrect(layer, win,
win.current["w"]/4+105,
15,
40,
40,
10,
do,
"frase_new",
talk.text("add_phrase_tooltip")+"\n\n[Ctrl - D]")
# ADD IMAGE
def do():
win.current["key_letter"] = chr(9)
UI_elements.roundrect(layer, win,
win.current["w"]/4+155,
15,
40,
40,
10,
do,
"image_link",
talk.text("add_image_script_tooltip")+"\n\n[Ctrl - I]")
# Next part is to add the scene name editor. It's not as easy as you might
# thing. Since every scene's shot might contain an actuall folder on the
# operating system after you create a first blend file to animate the shot.
# So there will be a folder for the scene with many folders for shots inside.
# And if you change the name of the scene in the story you probably want to
# change the name of the scene in the folder as well. BUT. Since there could
# be countless blend files inside. And some names just could be already taken
# and stuff like that, I will block the editing of the scene if a folder exists.
# And will give a folder icon.
# Technically it's going to be possible to rename the scene. By renaming the
# folder first. But this is a kind of breaking everything operation i don't
# want to implement inside the VCStudio it self. I give constant folder links
# so editing of files could be done by the user at any time.
# But I don't want it to feel easy. Since stuff like deleting assets or
# editing names could break things in a different place in the program. That's
# why I don't provide easy to use functions for it. The user should really
# decide to make a change like this. To the point of breaking the convinience
# of VCStudio.
# Parsing the url
if win.cur.count("/") > 1:
tmp = win.cur.replace("/","",1).split("/")
scene, shot = tmp[0], tmp[1]
else:
scene = win.cur[win.cur.rfind("/")+1:]
shot = ""
# Let's find out if a folder exists.
if os.path.exists(win.project+"/rnd/"+scene):
editable = False
minus = 50
# The minus is the width of the folder button
def do():
oscalls.Open(win.project+"/rnd/"+scene)
UI_elements.roundrect(layer, win,
win.current["w"]/4*3-45,
15,
40,
40,
10,
do,
"folder",
tip="/rnd/"+scene)
else:
editable = True
minus = 0
UI_elements.text(layer, win, "scene_name",
win.current["w"]/4+205,
15,
win.current["w"]/2-210-minus,
40,
set_text=scene,
editable=editable,
fill=False)
# Now let's do the actual edititing part. What we want to look for is whether
# the key already exists in the scenes, if not allow it to be applied. Now
# you are probably asking yourself. But the current scene exists there? So
# how could you apply it. Easy. If you change anything. It already doesn't
# exist there. And if you didn't change anything. What's the point of applying
# anyway? So here what we are going to do.
newname = win.text["scene_name"]["text"].replace("/","_").replace(" ", "_")\
.replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
.replace("[","_").replace("]","_").replace("{","_").replace("}","_")
if newname not in win.story["scenes"]:
def do():
win.story["scenes"][newname] = win.story["scenes"].pop(scene)
win.text["scene_name"]["text"] = newname
win.cur = newname
# There is one more rub. Scenes could be connected together.
# so now we need to preserve the connections as well. This is
# not that hard.
for arrow in win.story["arrows"]:
if arrow[0][1] == scene:
arrow[0][1] = newname
if arrow[1][1] == scene:
arrow[1][1] = newname
# Easy. I like how if it's a list. it's a link to the list.
# so there will not be memory overdrives. So I can itterate
# over a list of lists and assing values of to the itteration
# and not trying to get the main list. LOVELY.
# Saving to the file
story.save(win.project, win.story)
UI_elements.roundrect(layer, win,
win.current["w"]/4*3-45,
15,
40,
40,
10,
do,
"ok",
tip=talk.text("checked"))
# Just in case parsing the url again
if win.cur.count("/") > 1:
tmp = win.cur.replace("/","",1).split("/")
scene, shot = tmp[0], tmp[1]
else:
scene = win.cur[win.cur.rfind("/")+1:]
shot = ""
######### MAIN PART ###########
# Oh dear. I have no idea how hard it's going to be. But let's try. I don't
# thing it's going to be that insane. But I need to take in consideration
# a couple of things. Mainly text warping. Meaning I'm probably rendering
# each word as an individual object. Now this creates a challenge.
# How do I mark parts of the text having more then 1 word it it? I probably
# will need to draw a couple layers at ones.
# TEXT LAYER
# SHOTS MARKING LAYER
# ASSET MARKING LAYER
# And then combine them in a different order
# SHOT MARKING LAYER
# ASSET MARKING LAYER (on top)
# TEXT LAYER (on top of everything)
# So let's make those 3 layers.
x = win.current["w"]/4
y = 70
width = win.current["w"]/2 -30
height = win.current["h"]-130
# Good that at least all layers have the same size.
textsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
text = cairo.Context(textsurface)
text.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
shotsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
shots = cairo.Context(shotsurface)
shots.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
assetsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
assets = cairo.Context(assetsurface)
assets.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
frasesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
frases = cairo.Context(frasesurface)
frases.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
###########################
# Oh dear
# Clips:
UI_elements.roundrect(text, win, 0,0,width, height, 10,fill=False)
text.clip()
UI_elements.roundrect(shots, win, 0,0,width, height, 10,fill=False)
shots.clip()
UI_elements.roundrect(assets, win, 0,0,width, height, 10,fill=False)
assets.clip()
UI_elements.roundrect(frases, win, 0,0,width, height, 10,fill=False)
frases.clip()
# Background
UI_color.set(frases, win, "node_background")
frases.rectangle(0,0,width, height)
frases.fill()
# Scroll stuff
if "script" not in win.scroll:
win.scroll["script"] = 0
tileX = 12
current_Y = 0
# For highlights of assets and shots. Basically when mouse is over a given
# asset it will record itself into the higlight. And be read on the next
# frame by those above it which are links to the same asset / shot.
# This is done like so, so I could make multiline drawing of markings.
if "script_asset_higlight" not in win.current:
win.current["script_asset_higlight"] = ""
if "script_shot_higlight" not in win.current:
win.current["script_shot_higlight"] = ""
# Next. For editing the text we need a cursor on the screen. A kind of pointer
# that will be our selection. The only problem is that we are trying to make
# usefull text editor where you can select a part of text. And do things to it
# like let's say mark a shot or an asset. This is why there are actually 2
# points which are souce of quite heavy head auks later on.
pointer = [0,0] # The editing cursor
point = 0 # The current place in the text of the scene.
if scene in win.story["pointers"]: # Also I thought sinec we are
pointer = win.story["pointers"][scene] # saving selection in story
# editor. Why not save also
# The text pointers.
# Coming back to the previous thing. If I mouse over the part the part get's
# highlighted. But simply removing the mouse is not enough. Because I'm
# writting that it's higlighted into a global dictionary. So I want to clean.
# it ones in a while. And for now ( It does cause some visual noise ) I chose
# to remove the selection if the mouse cursor moved over 1 pixel from it's
# previous frame. So if you move the cursor very slowly, there will be no
# change.
if int(win.current["mx"]) not in range(int(win.previous["mx"])-1, int(win.previous["mx"])+1)\
and int(win.current["my"]) not in range(int(win.previous["my"])-1, int(win.previous["my"])+1):
win.current["script_asset_higlight"] = ""
win.current["script_shot_higlight"] = ""
# This 2 fellas will be responsible for drawing the shot marking.
shotpart = 0 # This is a pixel where to start the rounded rectangle.
shotis = "" # this is a current drawing shot's name.
# Same idea for the selection cursor.
ss = 0 # The pixel where to start drawing the selection cursor.
smx = ss
# Another one of these for the rectangles under frases. This one is Y location
# instead.
frasepart = 0
# This is the scroll when typing.
putscroll = 0
# Before we start. Let's bullet proof this. So if the scene does not exist
# we will be transferred back to story editor and not stuck.
try:
win.story["scenes"][scene]["shots"]
except:
win.url = "story_editor"
win.cur = ""
return surface
# Okay if nothing is in the scene we want to create a text block of 1 character
# into the scene. Because if it will have 0 characters it will be automatically
# deleted.
if not win.story["scenes"][scene]["shots"]\
or win.story["scenes"][scene]["shots"][-1][0] == "shot_block":
win.story["scenes"][scene]["shots"].append([
"text_block",[["text", ' ']]
])
# This is a list of parts that are inside the selection. I will populate it
# as I itterate trough text. The problem with this text is that it's not a
# long string. It's a list of blocks. Each of them has a string. But also
# each of them has some aditional metadata. Like is it a shot, or not. Is it
# a frase. Is it a link to an asset. This kind a thing. So in order to edit it
# I need to get all selected peaces into a big thing.
selecwords = []
# And a little thing for the left panel. Since we already itterating through.
# the shots. Why not get a quick list of them
shot_list = {}
# Okay so let's begin itterating the scene blocks. There are 2 types of scene
# blocks. (At least for now.) Those are shot_block and text_block. Text is a
# normal text parts. Shot is everything with in a shot. Shot has one more item
# in the list. It's the name of the shot. ['shot_block', 'shot_name', [<text>]]
# while text has just 2. ['text_block', [<text>]]. So you can see me refencing
# the block[-1] a lot. To get the text parts of the block.
for num, block in enumerate(win.story["scenes"][scene]["shots"]):
# If shot block is empty Let's get rig of it, so not to collect garbage in
# our story file. Each empty srting is still a string. That requires some
# characters to write it to file. So let's get rid of it here.
if not block[-1]:
del win.story["scenes"][scene]["shots"][num]
# Here I want to make another type of filtering. So there will not be
# 1 million blocks each 1 character long. I want to make it next.
# If the previous block has the same metadata as this one. Join them.
if num > 0 and win.story["scenes"][scene]["shots"][num-1][0] == "text_block" and block[0] == "text_block":
win.story["scenes"][scene]["shots"][num-1][-1] = win.story["scenes"][scene]["shots"][num-1][-1] + block[-1]
del win.story["scenes"][scene]["shots"][num]
# THIS ONE IS NOT TESTED YET, BUT I MIGHT ALSO FORGET TO DELETE THE COMMENT.
elif num > 0 and win.story["scenes"][scene]["shots"][num-1][0] == "shot_block" and block[0] == "shot_block"\
and win.story["scenes"][scene]["shots"][num-1][1] == block[1]:
win.story["scenes"][scene]["shots"][num-1][-1] = win.story["scenes"][scene]["shots"][num-1][-1] + block[-1]
del win.story["scenes"][scene]["shots"][num]
if block[0] == "shot_block" and block[1] not in shot_list:
shot_list[block[1]] = block
# Now other windows might want to reference the script. Like assets for
# example. So this script_find is for other windows to set. So while in
# the script it would automatically scroll to that part. And so if set
# only shot. Here is out shot finder, auto-scroller thingy.
if win.current["script_find"][0] and win.current["script_find"][0] == num:
win.scroll["script"] = 0-current_Y+height/2
win.current["script_find"][0] = 0
# Now let's itterate over parts of the text. Now you probably asking wasn't
# blocks just strings with some metadata? Yes and know. Every text block has
# blocks with in it. So I could mark items and images and frases with in text
# across or without a shot. Basically there is our simple Hello-World thing
# written inside a scene data. ['text_block', ['text','Hello-World']]
# This are similar. The actual sting is always in the very end. So to find it
# I'm using part[-1]
for n, part in enumerate(block[-1]):
# If you remember me deleting every empty block. Not this is the same
# but with empty parts. So you could delete a frase for example. While
# just backspacing.
if not part[-1]:
del block[-1][n]
# Here I want to make another type of filtering. So there will not be
# 1 million blocks each 1 character long. I want to make it next.
# If the previous block has the same metadata as this one. Join them.
if n > 0 and block[-1][n-1][0] == "text" and part[0] == "text":
block[-1][n-1][-1] = block[-1][n-1][-1]+part[-1]
part[-1] = ""
# Now this is our search finder auto-scroller again. But if a part
# also specified.
if win.current["script_find"][1] and win.current["script_find"][1] == n:
win.scroll["script"] = 0-current_Y+height/2
win.current["script_find"][1] = 0
# Now t and p. Well t is simply a shortcut to the string it self. Tho
# I found it not really usefull since it's not a link to the data. Like
# in the case of part[-1]. But for rendering it will be okay.
t = part[-1]
# Now p is a bit more complicated to explain. Basically I always need
# an index number from all character in the whole scene in relation to
# this particular part. See i will have a selection across the blocks
# and across the parts. And in order to edit them at the right place
# I need a reference point of where in the whole, this part starts.
p = point
# Now xb and mt you could see as margins of the text. Sometimes you
# want the whole width to be text. And sometimes. Like in frases you
# will want the text closer to the center.
xb = 12 # Where is the start of the line
mt = width # Where is the maximum width
##### FRASE #####
# Now let's make an example of xb and mt. Here is a frase. So when
# it's a frase. You want the text be closer to the center.
if part[0] == "frase":
# Give it some space. It's a rendering of a frase. You don't
# want to be a part of the rest of the text.
current_Y = current_Y + 30
# Now frase metadata has another part. Which could be either
# text or link. This is the name of our character. The little
# part at the top, in the center. If it's a link we want to
# draw it accordingly
if part[1][0] == "link":
# First we draw a little purple rectangle. To show us that
# it's a link to an item.
UI_color.set(assets, win, "node_asset")
UI_elements.roundrect(assets, win,
width/2-len(part[1][-1])*6-6,
win.scroll["script"] + current_Y+6,
len(part[1][-1])*12+12,
28,
10)
# Then we make a selection by hovering with the mouse. So if
# you click later. You will be transferred to the thing.
if int(win.current['mx']) in range(int(x+width/2-len(part[1][-1])*6-6), int(x+width/2-len(part[1][-1])*6-6+len(part[1][-1])*12+12)) \
and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
win.current["script_asset_higlight"] = part[1][1]
# And if the mouse is over the item's link. No matter if the
# link is this one. But if mouse is over any other mention of
# the same asset. We want to draw another rectangle. To give
# the user a feedback.
if part[1][1] == win.current["script_asset_higlight"]:
UI_color.set(assets, win, "text_normal")
UI_elements.roundrect(assets, win,
width/2-len(part[1][-1])*6-6,
win.scroll["script"] + current_Y+30,
len(part[1][-1])*12+12,
0,
3)
# Next we will want to do it make a little button to edit the
# name of the character. Because I guess I'm not smart enough
# to figure out how to make this a part of the script. And this
# implementation is not that terrible. I have a chance to put a
# single line entry into a multiline text. This is something.
if "current_name_frase_editing" not in win.current:
win.current["current_name_frase_editing"] = False
win.previous["current_name_frase_editing"] = False
# So we put a selection variable. And set up the button.
if not win.current["current_name_frase_editing"]:
def do():
win.current["LMB"] = False
win.previous["LMB"] = False
if not win.previous["current_name_frase_editing"]:
print(part[1][-1])
win.current["current_name_frase_editing"] = [num, n]
if "current_name_frase_editing" in win.text:
win.text["current_name_frase_editing"]["text"] = part[1][-1]
win.textactive = "current_name_frase_editing"
UI_elements.roundrect(frases, win,
0,
win.scroll["script"] + current_Y+6,
width,
28,
10,
button=do,
offset=[x,y])
# Now let's draw a little text editor with in a text editor. IK wild.
# but since everything is custom I can do wild things.
if win.current["current_name_frase_editing"] == [num, n]:
UI_elements.text(text, win, "current_name_frase_editing",
200,
win.scroll["script"] + current_Y-5,
width-400,
40,
set_text=part[1][-1],
offset=[x,y])
# Here I will add some logic to this little editor with in
# editor. OMG WHAT AM I DOING?
# First let's make the apply button. And I guess the enter key.
def linking():
print("linking")
if "linking_asset_operation_metadata" not in win.current:
win.current["linking_asset_operation_metadata"] = []
win.current["linking_asset_operation_metadata"] = part
def after(win, var):
print(var)
if var:
part = win.current["linking_asset_operation_metadata"]
print(part)
part[1] = ["link", var, part[1][-1]]
studio_dialogs.asset_select(win, "link_asset_script_frase", after,
SEARCH=win.text["current_name_frase_editing"]["text"])
UI_elements.roundrect(text, win,
width-240,
win.scroll["script"] + current_Y-5,
40,
40,
10,
button=linking,
icon="obj_link",
offset=[x,y])
def do():
part[1][-1] = win.text["current_name_frase_editing"]["text"]
win.current["current_name_frase_editing"] = False
win.current["LMB"] = False
win.previous["LMB"] = False
win.textactive = False
win.current["key_letter"] = ""
UI_elements.roundrect(text, win,
width-280,
win.scroll["script"] + current_Y-5,
40,
40,
10,
button=do,
icon="ok",
offset=[x,y])
# Enter key.
if 65293 in win.current["keys"]:
do()
# If you press Ctrl-Enter instead of Enter. I want it
# to apply and also give you automatically to select
# the asset to link in the frase header.
if 65507 in win.current["keys"]:
linking()
win.current["keys"] = []
# Canceling the editing. If we don't do that the entry thing
# will still be on the screen. And it's not something that
# I want to have there.
if not win.textactive:
win.current["current_name_frase_editing"] = False
win.current["key_letter"] = ""
else:
# And if we are not editing the name currently
# let's just draw the name in the center of the
# screen.
UI_color.set(text, win, "text_normal")
text.set_font_size(20)
text.move_to(
width/2-len(part[1][-1])*6,
win.scroll["script"] + current_Y+25,
)
text.show_text(part[1][-1])
# Giving it a new line.
current_Y = current_Y + 35
# Putting the line into the frasepart so to draw a rectangle
# later under the whole thing.
frasepart = current_Y
# And here we are editing the margins. Because actuall text will
# be done in a different place.
xb = 212
mt = width - 200
tileX = xb
# And passing the current starting position to selection drawing
# so it too will start where it should.
if ss:
ss = xb
# Now if we are not in the frase. Let's draw the damn rectangle under
# it finally. # THIS IS STILL BUGGY.
if frasepart and part[0] != "frase":
UI_color.set(frases, win, "dark_overdrop")
UI_elements.roundrect(frases, win,
200,
win.scroll["script"] + frasepart,
width-400,
current_Y - frasepart+40,
10)
frasepart = 0
# MAKING A NEW LINE
smx = tileX
tileX = xb
if assetpart:
assetpart = xb
if shotpart:
shotpart = xb
if ss:
UI_color.set(assets, win, "node_blendfile")
UI_elements.roundrect(assets, win,
ss,
win.scroll["script"] + current_Y+5,
smx-ss,
26,
10)
smx = ss
ss = xb
current_Y = current_Y + 30
# Now let's draw our images. If they are in the scene. # THIS IS STILL BUGGY
if part[0] == "image":
if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+1]):
if win.current["key_letter"] and not win.textactive:
# Let's make it possible to delete images by just
# deleting near them.
part[-1] = ""
current_Y = current_Y + 30
if os.path.exists(win.project+t):
UI_elements.image(text, win, win.project+t,
100,
win.scroll["script"] + current_Y,
int(width)-200,
380,
cell="script_images")
else:
UI_elements.image(text, win, t,
100,
win.scroll["script"] + current_Y,
int(width)-200,
380,
cell="script_images")
# Now I guess to simplify the matters let's create a selection
# button. Double clicking which will open the file.
# So we need a selection dialog. I think I'm going to hack into
# my own program right now because I'm lazy to implement anything
# normal. This image thing. Will need more love later on. Now it's
# thrown together quite in a rush. I would say.
def do():
if t != win.textactive:
win.textactive = t
else:
oscalls.file_open(win, t)
UI_elements.roundrect(text, win,
100,
win.scroll["script"] + current_Y,
int(width)-200,
380,
10,
button=do,
fill=False,
offset=[x,y])
text.stroke()
# Selection drawing
if t == win.textactive:
UI_color.set(text, win, "text_normal")
UI_elements.roundrect(text, win,
100,
win.scroll["script"] + current_Y,
int(width)-200,
380,
10,
fill=False)
text.stroke()
# Removing it from the selection using ESC
if 65307 in win.current["keys"]:
win.textactive = ""
win.current["keys"] = []
win.current["key_letter"] = ""
# Deleting the image if pressing Delete or Backspace
if 65288 in win.current["keys"] or 65535 in win.current["keys"]:
win.textactive = ""
part[-1] = ""
win.current["keys"] = []
win.current["key_letter"] = ""
current_Y = current_Y + 400
continue # we don't want to image url in the text it self.
# Do you remember I was doing the starting positions of shots and
# selection? These 2 are the same concept but to links to assets.
assetpart = 0
assetis = ""
############## TEXT IT SELF ###############
# Here we go and render the text. Now you can see I iterate over
# lines using re.split rather then built in split(). It's because
# I need all characters still be present. Since I'm counting every
# each character.
for line in re.split("(\n)",t):
# Now if the line says '\n' (new_line) then I just want to
# draw the next line. Note that I'm also drawing the selection
# box here. And moving all assetpart, shotpart and ss. So on
# the next line those rectangles would start their drawing from
# the begining of the line.
# Also not the large comment with a POINT in it. It's I'm counting
# this character in for to know where I'm editing.
if line == "\n":
smx = tileX
tileX = xb
#point = point + 1 #################### <<<<<< POINT
if assetpart:
assetpart = xb
if shotpart:
shotpart = xb
if ss:
UI_color.set(assets, win, "node_blendfile")
UI_elements.roundrect(assets, win,
ss,
win.scroll["script"] + current_Y+5,
smx-ss,
26,
10)
text.stroke()
smx = ss
ss = xb
current_Y = current_Y + 30
line = " "
#continue # And this is since I don't want weird rectangles
# on the screen. And just new lines.
# Now let's itterate over each and every word. Since we want
# text wrapping. So if a certain word goes off screen I want
# to declare a next line. NOTE: I'm still using re.split()
# becuase we are counting charaters still.
for word in re.split("( )", line):
# Now let's do the actuall text wrapping. For size 20 which
# is what I'm using. With monospace font. Width of each
# character is 12 pixels. ( More or less. Good enough for
# what we are doing ). So if lenght of word times 12, plus
# current place on X is more then the width of our screen,
# with all the margins. We want to start a new line.
if tileX + len(word)*12 > mt:
smx = tileX
tileX = xb
# And do not forget about all the asset, shot and selection
# rendering too.
if assetpart:
assetpart = xb
if shotpart:
shotpart = xb
if ss:
UI_color.set(assets, win, "node_blendfile")
UI_elements.roundrect(assets, win,
ss,
win.scroll["script"] + current_Y+5,
smx-ss,
26,
10)
text.stroke()
ss = xb
current_Y = current_Y + 30
# This is logic to draw ASSETS higlights.
if part[0] == "link" and part[1] != assetis and not assetpart:
assetpart = tileX
assetis = part[1]
if assetpart:
if word:
UI_color.set(assets, win, "node_asset")
UI_elements.roundrect(assets, win,
assetpart-5,
win.scroll["script"] + current_Y+8,
tileX - assetpart + len(word)*12+12,
22,
10)
# Selectiong asset. ( Not actually.) But making a
# highlight of the asset. And if clicked. Going
# to the asset.
if int(win.current['mx']) in range(int(x+assetpart-5), int(x+tileX + len(word)*12+12)) \
and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
win.current["script_asset_higlight"] = assetis
if assetis == win.current["script_asset_higlight"]:
UI_color.set(assets, win, "text_normal")
UI_elements.roundrect(assets, win,
assetpart-6,
win.scroll["script"] + current_Y+26,
tileX - assetpart + len(word)*12+12,
0,
3)
assetpart = tileX
assetis = part[1]
if part[0] != "link":
assetpart = 0
assetis = ""
# And this is logic to draw SHOTS higlights.
if block[0] == "shot_block" and block[1] != shotis and not shotpart:
shotpart = tileX
shotis = block[1]
if shotpart:
if word:
# I want to randomize the shots colors. But I don't it to be an epileptic
# show. But not too much. Let's actually write the colors into the story
# data. Why not.
if "shot_colors" not in win.story:
win.story["shot_colors"] = {}
surl = "/"+scene+"/"+shotis
# Making it scroll to the selected shot in the story itself.
# Thankyou very much...
if "scroll_shot_to_in_script" not in win.current:
win.current["scroll_shot_to_in_script"] = True
if surl == win.cur and win.current["scroll_shot_to_in_script"]:
win.scroll["script"] = (0 - current_Y) + (win.current["h"]/2)
win.current["scroll_shot_to_in_script"] = False
if surl not in win.story["shot_colors"]:
rcolors = [
"shot_1",
"shot_2",
"shot_3",
"shot_4",
"shot_5"
]
win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
col = win.story["shot_colors"][surl]
if int(win.current['mx']) in range(int(x+shotpart-5), int(x+tileX + len(word)*12+12)) \
and int(win.current['my']) in range(int(y+win.scroll["script"] + current_Y+8), int(y+win.scroll["script"] + current_Y+8+22)) :
win.current["script_shot_higlight"] = shotis
UI_color.set(shots, win, col)
UI_elements.roundrect(shots, win,
shotpart-6,
win.scroll["script"] + current_Y+5,
tileX - shotpart + len(word)*12+12,
28,
10)
# This is a bit of a hack because I was lazy to
# figure out how to make shot markings as good
# as I made the selection. So drawing a thing around it
# would not really work that well. But a line under the
# text looks quite alright. I like the look of it.
# Maybe I will polish this in future and give people
# an option to draw it differently. I don't know yet.
if shotis == win.current["script_shot_higlight"] or shot == shotis:
UI_color.set(shots, win, "text_normal")
UI_elements.roundrect(shots, win,
shotpart-6,
win.scroll["script"] + current_Y+30,
tileX - shotpart + len(word)*12+12,
0,
3)
shotpart = tileX
shotis = block[1]
if block[0] != "shot_block":
shotpart = 0
shotis = ""
# Now let's draw our word. Simple really. What's so special
# about it? Why is this comment 2 lines high?
UI_color.set(text, win, "text_normal")
#if win.current["script_shot_higlight"] == block[1] or shot == block[1]:
# UI_color.set(text, win, "darker_parts")
text.set_font_size(20)
text.move_to(
tileX,
win.scroll["script"] + current_Y+25,
)
text.show_text(word)
# Here I want to make a selection using a mouse. I think it's quite important
# since using a Shift key and pressing sideways is not cool
# and I want it to be cool.
if win.current["LMB"]:
if int(win.current["LMB"][1]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
and int(win.current["LMB"][0]-x) in range(tileX, tileX+len(word)*12+12):
pointer[0] = int(point - (tileX+len(word)*12 - int(win.current["LMB"][0]-x))/12 + len(word))
if int(win.current["my"]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
and int(win.current["mx"]-x) in range(tileX, tileX+len(word)*12+12):
pointer[1] = int(point - (tileX+len(word)*12 - int(win.current["mx"]-x))/12 + len(word))
win.cur = "/"+scene
win.textactive = ""
if int(win.current["my"]) in range(int(y+win.scroll["script"] + current_Y), int(y+win.scroll["script"] + current_Y)+25)\
and int(win.current["mx"]-x) in range(tileX, tileX+len(word)*12+12):
win.current["cursor"] = win.cursors["text"]
# This is the logic to draw our selection and logic that
# come with the selection.
if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+len(word)+1]) and not ss:
ss = tileX+(min(pointer[1], pointer[0])-point)*12
smx = ss
# If current word is inside the selection. It means that
# current text part is also inside the selection. And this
# means this part should be edited if I press a button.
if UI_math.line_overlap([pointer[1], pointer[0]],[point, point+len(word)+1]):
# So we need to add this part and some data about this
# part like amount of characters it's away from the
# begining of the scene into a list that I could reference
# later in the writting logic.
if [part, p, num, n] not in selecwords:
selecwords.append([part, p, num, n])
# Let's make something for me to understand what is going
# on. If I will enter the testing mode. Let it draw the
# current number of characters. And the current characrter.
if win.current["testing"]:
# This is our character. It's not always available.
# sometimes you migt be on the edge of something and
# something else. So !! will mean seomthing is wrong.
try:
c = part[-1][pointer[0]-p]
except:
c = "!!"
UI_color.set(text, win, "text_normal")
text.set_font_size(10)
text.move_to(
tileX+(pointer[0]-point)*12-26,
win.scroll["script"] + current_Y+35,
)
text.show_text(str(pointer[0])+"["+c+"]")
# Now doing it again for the second pointer. If you
# remeber, We have 2.
try:
c = part[-1][pointer[1]-p]
except:
c = "!!"
UI_color.set(text, win, "text_normal")
text.set_font_size(10)
text.move_to(
tileX+(pointer[1]-point)*12-26,
win.scroll["script"] + current_Y+35,
)
text.show_text(str(pointer[1])+"["+c+"]")
if max(pointer[1], pointer[0]) in range(point, point+len(word)+1):
# This is some more drawing of the selection. This is
# if the selection is ending mid way through the line.
if ss:
if pointer[0] != pointer[1] :
UI_color.set(assets, win, "node_blendfile")
UI_elements.roundrect(assets, win,
max(ss, xb),
win.scroll["script"] + current_Y+5,
min(tileX+(max(pointer[1], pointer[0])-point)*12-max(ss, xb), mt-max(ss, xb)),
26,
10)
elif not win.textactive and win.blink:
UI_color.set(text, win, "node_blendfile")
text.rectangle(
max(ss, xb),
win.scroll["script"] + current_Y+5,
min(tileX+(max(pointer[1], pointer[0])-point)*12-max(ss, xb), mt-max(ss, xb)),
26)
text.stroke()
# win.scroll["script"] + current_Y,
# min(mt-max(ss,xb), mt-xb),
ss = 0
#smx = 0
if win.current["keys"] and not win.textactive and not putscroll:
putscroll = min(0 - current_Y + width/2, win.scroll["script"])
# Now in the end of our word. We are counting it's lenght
# and moving the tileX so the next word draws after this
# one and not on top of this one. I think it's important.
point = point + len(word) #################### <<<<<< POINT
tileX = tileX + len(word)*12
# Also what you probably want to happen is when you type the script
# will scroll down. Yeah. It's not coming by default. Even on standart
# GTK text parts. So here is a little thing.
if putscroll:
win.scroll["script"] = putscroll
############# LOGIC ###############
# So I'm doing the logic of typing separatelly from rendering of the text.
# because it kind a seems right to do so. I'm still figuring it out at this
# point. I have no idea what could go wrong. Maybe I will reddo the whole
# thing. Maybe few times. IDK.
if 65363 in win.current["keys"]: # RIGHT
pointer[0] = pointer[0]+1
if not 65506 in win.current["keys"]:
pointer[1] = pointer[0]
#win.current["keys"].remove(65363)
if 65361 in win.current["keys"]: # LEFT
pointer[0] = pointer[0]-1
if not 65506 in win.current["keys"]:
pointer[1] = pointer[0]
#win.current["keys"].remove(65361)
if 65362 in win.current["keys"]: # UP
pointer[0] = pointer[0]-min(len(line), int((mt-12)/12)) # STILL BUGGY
if not 65506 in win.current["keys"]:
pointer[1] = pointer[0]
#win.current["keys"].remove(65362)
if 65364 in win.current["keys"]: # DOWN
pointer[0] = pointer[0]+min(len(line), int((mt-12)/12)) # STILL BUGGY
if not 65506 in win.current["keys"]:
pointer[1] = pointer[0]
#win.current["keys"].remove(65364)
# In order for history to work properly I don't want to save history on
# each button press. But rather ones every time the scene is edited. So
if "scene_edited_already" not in win.current:
win.current["scene_edited_already"] = False
# I'm going to implement some basic clipboard. It will need love in future.
# I just need for now something that will "work" so to speak. See where I
# use the clipboard variable later to learn how it all works.
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) # This is a clipboard object
# If there is a key press that contains a string. Like anything on a keyboard
# where you can actually type. Let's do something with it.
def clean():
# This is an empty function. Placeholder for a clean operation after
# a given key press. Because we are dealing with multiple blocks of
# text that look like 1 block for the user.
pass
# To make shot I want to write a little list of text parts that will come
# into that shot.
new_shot_list = []
after_shot_list = []
new_shot = []
if win.current["key_letter"] and not win.textactive:
# Here I'm going to implement history
if not win.current["scene_edited_already"]:
win.current["scene_edited_already"] = True
history.record(win, "/rnd"+win.cur, "[Edited]")
#print("------{ BEFORE }--------")
#print(win.story["scenes"][scene]["shots"])
#print("------{ }------")
#print()
#print() # For testing
for u, stuff in enumerate(selecwords):
part, p, num, n = stuff
try:
block = win.story["scenes"][scene]["shots"][num]
except:
break
try:
ORD = ord(win.current["key_letter"])
except:
ORD = 0
print(ORD, "ORD") # Output the value of the button.
# Now some of the ORDs I found to be weird characters. Like on
# CTRL - C or CTRL - V. So I want to filter them out. So there
# wont be a bunch of rectangles every time you type.
# Now I need the minimum and maximum parts. So there will not be
# scips of content I guess.
MIN = min(len(part[-1]), max(0, min(pointer[1], pointer[0])-p))
MAX = max(0, min(len(part[-1]), max(pointer[1], pointer[0])-p))
#print(pointer,"POINTER")
#print(p, "P")
#print(len(part[-1]), "LENGHT", [part[-1]])
#print(MIN, "MIN")
#print(MAX, "MAX")
#print()
# Here I want to record undo history for the script writer. I am going
# to ignore the letters. And only forcus on other simbols such as
# spacebars and various commands.
nonhistory = "1234567890qwertyuiopasdfghjklzxcvbnnmQWERTYUIOPASDFGHJKLZXCVBNM"
if win.current["key_letter"] not in nonhistory and ORD not in [26, 25]:
story.undo_record(win)
# Multiuser sycning
win.multiuser["request"] = "story"
# Now let's retrive out data.
# UNDO
if ORD == 26:
story.undo(win)
# REDO
if ORD == 25:
story.redo(win)
if u == 0:
if ORD not in range(32) or ORD in range(127, 160):
# Typing logic
part[-1] = \
part[-1][:MIN]+\
win.current["key_letter"]+\
part[-1][MAX:]
def clean():
pointer[0] = pointer[0] + 1
pointer[1] = pointer[0]
point = point + 1
if ORD == 22: # Paste ( Ctrl - V )
cliptext = str(clipboard.wait_for_text())
part[-1] = \
part[-1][:MIN]+\
cliptext+\
part[-1][MAX:]
def clean():
pointer[0] = pointer[0] + len(cliptext)
pointer[1] = pointer[0]
point = point + len(cliptext)
if ORD == 3: # Copy ( Ctrl - C )
clipboard.set_text( part[-1][MIN:MAX] , -1) # VERY BASIC
# If ord 13 it means you pressed enter. And it should be a new line.
# but for some reason it's ord 13. Maybe it is a new line. Who knows
# Anyway. It does not return \n but returns a weird rectangle. So
# here is a little solution.
if ORD == 13: # ENTER
# Only I want to use new line to exit from all kind of things.
# Like double enter will exit the frase. Ans stuff like this.
if part[-1][MIN-1] == "\n" and part[0] != "text":
block[-1].insert(n+1,
["text", "\n"]
)
block[-1].insert(n+2,
["text", part[-1][MAX:]+" "]
)
part[-1] = \
part[-1][:MIN-1]
else:
part[-1] = \
part[-1][:MIN]+\
"\n"+\
part[-1][MAX:]
def clean():
pointer[0] = pointer[0] + 1
pointer[1] = pointer[0]
point = point + 1
if ORD == 4: # CTRL D ( FOR SOME REASON)
block[-1].insert(n+1,
["frase", ["text", " "], " "]
)
block[-1].insert(n+2,
["text", part[-1][MAX:]]
)
part[-1] = \
part[-1][:MIN]
def clean():
pointer[0] = pointer[0] + 1
pointer[1] = pointer[0]
point = point + 1
# Activating the editing of the name automatically
if "current_name_frase_editing" in win.text:
win.text["current_name_frase_editing"]["text"] = ""
if "current_name_frase_editing" not in win.current:
win.current["current_name_frase_editing"] = False
win.current["current_name_frase_editing"] = [num, n+1]
win.textactive = "current_name_frase_editing"
if ORD == 8: # BACKSPACE
if pointer[1] == pointer[0]:
part[-1] = \
part[-1][:MIN-1]+\
part[-1][MAX:]
def clean():
pointer[0] = pointer[0] - 1
pointer[1] = pointer[0]
point = point - 1
else:
part[-1] = \
part[-1][:MIN]+\
part[-1][MAX:]
def clean():
pointer[0] = min(pointer[0], pointer[1])
pointer[1] = pointer[0]
if ORD == 127: # DELETE (FORWARD)
part[-1] = \
part[-1][:MIN]+\
part[-1][MAX+1:]
def clean():
pointer[0] = pointer[0]
pointer[1] = pointer[0]
if ORD == 19: # NEW SHOT ( CTRL - S )
app = part.copy()
app[-1] = part[-1][MIN:MAX]
new_shot_list.append(app)
new_shot = [num , n]
after_shot_list = block.copy()
after_shot_list[-1] = []
app2 = part.copy()
app2[-1] = part[-1][MAX:]
after_shot_list[-1].append(app2)
part[-1] = part[-1][:MIN]
if ORD == 12 and part[0] == "text" and pointer[1] != pointer[0]: # Asset higlight
if "linking_asset_operation_metadata" not in win.current:
win.current["linking_asset_operation_metadata"] = []
win.current["linking_asset_operation_metadata"] = [part , block, MIN, MAX]
def after(win, var):
print(var)
if var:
part , block, MIN, MAX = win.current["linking_asset_operation_metadata"]
block[-1].insert(n+1,
["link", var, part[-1][MIN:MAX]]
)
block[-1].insert(n+2,
["text", part[-1][MAX:]]
)
part[-1] = \
part[-1][:MIN]
win.current["key_letter"] = ""
studio_dialogs.asset_select(win, "link_asset_script_frase", after,
SEARCH=part[-1][MIN:MAX])
if ORD == 9 and part[0] == "text" and pointer[1] == pointer[0]: # Insert image
if "linking_asset_operation_metadata" not in win.current:
win.current["linking_asset_operation_metadata"] = []
win.current["linking_asset_operation_metadata"] = [part , block, MIN, MAX]
def after(win, var):
print(var)
if var:
part , block, MIN, MAX = win.current["linking_asset_operation_metadata"]
block[-1].insert(n+1,
["image", var]
)
block[-1].insert(n+2,
["text", part[-1][MAX:]]
)
part[-1] = \
part[-1][:MIN]
win.current["key_letter"] = ""
studio_dialogs.file_select(win, "link_asset_script_frase", after,
SEARCH=part[-1][MIN:MAX])
# Making stuff happen
elif u < len(selecwords)-1:
if ORD not in [12, 19, 3, 22, 26, 25]: # 3 = Crtl - C, 22 = Ctrl - V
part[-1] = ""
if ORD == 19:
app = part.copy()
new_shot_list.append(app)
part[-1] = ""
else:
MIN = min(len(part[-1])-1, max(0, min(pointer[1], pointer[0])-p))
MAX = max(0, min(len(part[-1])-1, max(pointer[1], pointer[0])-p))
if ORD not in [12, 19, 3, 22, 26, 25]:
part[-1] = part[-1][MAX:]
if ORD == 19:
app = part.copy()
app[-1] = part[-1][MIN:]
new_shot_list.append(app)
part[-1] = ""
# Saving to the file
story.save(win.project, win.story)
win.current["key_letter"] = ""
# Let's get all the rest of the parts from that block so I could insert them
# as a separate thing later. Because we basically splitting 1 block into 3
# making the selection. Middle one our new shot_block. And the rest. Should
# stay the same.
if new_shot:
num , n = new_shot
for l, part in enumerate(win.story["scenes"][scene]["shots"][num][-1]):
if l > n:
app = part.copy()
after_shot_list[-1].append(app)
part[-1] = ""
if after_shot_list[0] == "shot_block"\
and win.story["scenes"][scene]["shots"][num][0] == "shot_block":
after_shot_list[1] = win.story["scenes"][scene]["shots"][num][1]
if new_shot_list:
#print("NEW: ", new_shot_list)
#print("KEEP: ", after_shot_list)
nename = "Shot_1"
count = 1
while nename in shot_list:
count = count + 1
nename = "Shot_"+str(count)
num , n = new_shot
win.story["scenes"][scene]["shots"].insert(num+1,
["shot_block", nename, new_shot_list])
win.story["scenes"][scene]["shots"].insert(num+2,
after_shot_list)
# Saving to the file
story.save(win.project, win.story)
#print()
#print("------{ AFTER }--------")
#print(win.story["scenes"][scene]["shots"])
#print("------{ }------")
clean()
current_Y = current_Y + 30
if scene not in win.story["pointers"]:
pointer = [point, point]
win.story["pointers"][scene] = pointer
else:
pointer[0] = min(pointer[0], point)
pointer[1] = min(pointer[1], point)
pointer[0] = max(pointer[0], 1)
pointer[1] = max(pointer[1], 1)
win.story["pointers"][scene] = pointer
# Selecting the shot
if win.current["script_shot_higlight"] and win.previous["LMB"] and not win.current["LMB"]:
win.cur = "/"+scene+"/"+win.current["script_shot_higlight"]
win.current["shot_left_side_scroll_please_work_omg_wtf"] = True
# Going to that asset
if win.current["script_asset_higlight"] and win.previous["LMB"] and not win.current["LMB"]:
try:
del win.text["scene_name"]
win.previous["script_asset_higlight"]
win.url = "assets"
win.cur = win.current["script_asset_higlight"]
win.current["asset_scene_selected"] = scene
win.current["asset_left_panel"] = "scene"
del win.current["script_asset_higlight"]
# Saving to the file
story.save(win.project, win.story)
except:
pass
###########################
# And finally combine the layers
# Outputting the layer
layer.set_source_surface(frasesurface, x, y)
layer.paint()
# Outputting the layer
layer.set_source_surface(shotsurface, x, y)
layer.paint()
# Outputting the layer
layer.set_source_surface(assetsurface, x, y)
layer.paint()
# Outputting the layer
layer.set_source_surface(textsurface, x, y)
layer.paint()
# Scroll
UI_elements.scroll_area(layer, win, "script",
x+0,
y+0,
width+30,
height-0,
current_Y,
bar=True,
mmb=True,
bar_always=True)
####### BOTTOM PANEL #########
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
win.current["w"]/4,
win.current["h"]-50,
win.current["w"]/2,
40,
10)
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_script_writer"))
UI_elements.roundrect(layer, win,
win.current["w"]/4,
win.current["h"]-50,
40,
40,
10,
do,
"question")
# Here in the bottom I want to introduce little things to go between scenes
# back and forth.
left = ""
right = ""
for arrow in win.story["arrows"]:
if arrow[0][1] == scene and arrow[1] != "end":
right = arrow[1][1]
if arrow[1][1] == scene and arrow[0] != "start":
left = arrow[0][1]
if left:
def do():
win.scroll["script"] = 0
win.cur = "/"+left
win.url = "script"
try:
del win.text["scene_name"]
except:
pass
UI_elements.roundrect(layer, win,
win.current["w"]/2-45,
win.current["h"]-50,
40,
40,
10,
button=do,
icon="left")
if right:
def do():
win.scroll["script"] = 0
win.cur = "/"+right
win.url = "script"
try:
del win.text["scene_name"]
except:
pass
UI_elements.roundrect(layer, win,
win.current["w"]/2+5,
win.current["h"]-50,
40,
40,
10,
button=do,
icon="right")
# CANCEl
def do():
win.url = "story_editor"
win.assets = {}
try:
del win.text["scene_name"]
except:
pass
win.story["selected"] = [["scene", scene]]
win.scroll["script"] = 0
win.current["scene_edited_already"] = False
UI_elements.roundrect(layer, win,
win.current["w"]-40-win.current["w"]/4,
win.current["h"]-50,
40,
40,
10,
button=do,
icon="cancel",
tip=talk.text("cancel"))
# Short cut ESC
if 65307 in win.current["keys"] and not win.textactive:
do()
win.current["keys"].remove(65307)
############# CHECKLIST #################
# Here I want to have some checklists. Now... I have 4 types of new check-
# lists prepared for shots and scene. There are 3 types of checklists for
# a shot. So I can't just simply generate one on the fly. Since I don't
# know whether this shot is a simple shot, animated or VFX. And also I
# don't want to generate folders when they are not nessesary. So I want to
# implement a kind of selection thingy when if a checklists does not exist.
# Now let's first of all display our checklists properly.
if os.path.exists(win.project+"/rnd"+win.cur+"/shot.progress"):
checklist.draw(layer, win, win.project+"/rnd"+win.cur+"/shot.progress", back=win.url)
elif os.path.exists(win.project+"/rnd"+win.cur+"/scene.progress") and not shot:
checklist.draw(layer, win, win.project+"/rnd"+win.cur+"/scene.progress", back=win.url)
else:
# Now if we have no checklist what so ever. We want to give user a way
# to choose to generate one.
x = win.current["w"] / 4 * 3 + 10
y = 10
width = win.current["w"] / 4 - 20
height = win.current["h"] - 20
if shot:
# If we are in a shot I want to give different option to choose from
# then if we are just simply in a scene.
shot_options = {
"shot":"add_shot_live_checklist",
"shot_anim":"add_shot_anim_checklist", # First name of new_file then text on screen
"shot_vfx":"add_shot_vfx_checklist"
}
for num, option in enumerate(shot_options):
# We want to draw a button, a rectangle arround it and the text
# tooltip.
def do():
# First let's make sure that the folder exists. Because
# it might not exists. I think doing it automatically
# makes sense.
try:
os.makedirs(win.project+"/rnd"+win.cur)
except:
pass
# Then we copy the file.
oscalls.copy_file(
win,
os.getcwd()+"/new_file/"+option+".progress",
"/rnd"+win.cur+"/",
"shot.progress")
UI_elements.roundrect(layer, win,
x,
y+(50*num),
width,
40,
10,
button=do,
icon="checklist_new")
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x,
y+(50*num),
width,
40,
10,
fill=False)
layer.stroke()
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to( x+50, y+(50*num)+25)
layer.show_text(talk.text(shot_options[option]))
# Add checklist from clipboard
def do():
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
cliptext = str(clipboard.wait_for_text())
savetext = ""
for i in cliptext.split("\n"):
if "[ ]" in i or "[V]" in i or "[v]" in i:
savetext = savetext + "\n"+i.replace("[V]", "[ ]").replace("[v]", "[ ]")
else:
savetext = savetext + "\n[ ] #"+i
cliptext = savetext[1:]
filepath = win.project+"/rnd"+win.cur+"/shot.progress"
s = open(filepath, 'w')
s.write(cliptext)
s.close()
UI_elements.roundrect(layer, win,
x,
y+(50*num+50),
width,
40,
10,
button=do,
icon="checklist_new")
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x,
y+(50*num+50),
width,
40,
10,
fill=False)
layer.stroke()
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to( x+50, y+(50*num+50)+25)
layer.show_text(talk.text("new_checklist_from_clipboard"))
else:
# Now... If it's not a shot. We can do it in one line.
# It's pretty simple.
def do():
# First let's make sure that the folder exists. Because
# it might not exists. I think doing it automatically
# makes sense.
try:
os.makedirs(win.project+"/rnd"+win.cur)
except:
pass
# Then we copy the file.
oscalls.copy_file(
win,
os.getcwd()+"/new_file/scene.progress",
"/rnd"+win.cur+"/",
"scene.progress")
UI_elements.roundrect(layer, win,
x,
y,
width,
40,
10,
button=do,
icon="checklist_new")
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x,
y,
width,
40,
10,
fill=False)
layer.stroke()
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to( x+50, y+25)
layer.show_text(talk.text("add_scene_checklist"))
############## LEFT PANEL #####################
leftpanellist = ["shot", "schedule", "history"] # Using the names of the icons.
# We need to choose the correct category based smartly on the project's
# current progress. Or at least on the current progress of this asset.
if "script_left_panel" not in win.current:
win.current["script_left_panel"] = "shot"
# A little banner.
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
10,
10,
win.current["w"]/4-20,
50,
10)
for num, thing in enumerate(leftpanellist):
if win.current["script_left_panel"] == thing:
UI_color.set(layer, win, "progress_time")
UI_elements.roundrect(layer, win,
20+(40*num),
15,
40,
40,
10)
def do():
win.current["script_left_panel"] = thing
UI_elements.roundrect(layer, win,
20+(40*num),
15,
40,
40,
10,
do,
thing)
### SCHEDULES ###
if win.current["script_left_panel"] == "schedule":
schedule.draw(layer, win)
### HISTORY ###
if win.current["script_left_panel"] == "history":
history.draw(layer, win)
#### SHOTS ####
if win.current["script_left_panel"] == "shot":
# Here I want to draw the left panel full of shots and stuff. So you
# could access the animation Blend-Files, render them and see analytics
# or rendering.
# Since it's the last thing I'm drawing to this layer. I guess we can clip it.
# Small progress bar of the scene:
# # Progressbar
scenedone = win.story["scenes"][scene]["fraction"]
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
150,
25,
win.current["w"]/4-180,
20,
10,
tip="Entire scene: "+str(round(scenedone*100, 1))+"%")
# Project Done
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
150,
25,
(win.current["w"]/4-180)*scenedone,
20,
10)
x = 10
y = 70
width = win.current["w"] / 4 - 20
height = win.current["h"] - 80
UI_elements.roundrect(layer, win,
x,
y,
width,
height,
10,
fill=False)
layer.clip()
if "script_shots" not in win.scroll:
win.scroll["script_shots"] = 0
current_Y_shots = 0
rcolors = [
"shot_1",
"shot_2",
"shot_3",
"shot_4",
"shot_5"
]
# So let's itterate over a list of shots we've got from the textview.
for shotis in shot_list:
# Getting the color. It's not always works.
if "shot_colors" not in win.story:
win.story["shot_colors"] = {}
surl = "/"+scene+"/"+shotis
if "shot_left_side_scroll_please_work_omg_wtf" not in win.current:
win.current["shot_left_side_scroll_please_work_omg_wtf"] = False
if win.current["shot_left_side_scroll_please_work_omg_wtf"] and win.cur == surl:
win.scroll["script_shots"] = 0-current_Y_shots+60
win.current["shot_left_side_scroll_please_work_omg_wtf"] = False
if surl not in win.story["shot_colors"]:
win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
col = win.story["shot_colors"][surl]
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
x,
y+win.scroll["script_shots"]+current_Y_shots,
width,
50,
10)
UI_color.set(layer, win, col)
UI_elements.roundrect(layer, win,
x+60,
y+win.scroll["script_shots"]+current_Y_shots+15,
10,
10,
10)
# SHOT NAME
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to( x+90, y+win.scroll["script_shots"] + current_Y_shots+30)
layer.show_text(shotis)
# ICON
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/shot.png",
20, y+win.scroll["script_shots"] + current_Y_shots+5, 40, 40)
# The selection will be badically the current win.cur value. SO...
if surl == win.cur:
UI_color.set(layer, win, "text_normal")
UI_elements.roundrect(layer, win,
x,
y+win.scroll["script_shots"]+current_Y_shots,
width,
50,
10,
fill=False)
layer.stroke()
# If this shot has a folder already. Unfortunatly it's not wise
# to make it editable. But I already talk about it in the
# beggining of this file.
# But a folder button could be cool.
if os.path.exists(win.project+"/rnd"+win.cur):
def do():
oscalls.Open(win.project+"/rnd"+win.cur)
UI_elements.roundrect(layer, win,
width-45,
y+win.scroll["script_shots"]+current_Y_shots+5,
40,
40,
10,
do,
"folder",
tip="/rnd"+win.cur )
else:
# If there is no folder I want to give people an ability
# to edit it. I think let's do it like with checklists.
# like a little second button. That if you click it. You
# create a text entry. I know. Quite a lot of text entries.
# There will be more.
if "current_shot_name_editor" not in win.current:
win.current["current_shot_name_editor"] = ""
# Now if the button is clicked. There will be an editor
# let's make one.
if win.current["current_shot_name_editor"] == shotis and win.textactive == "shot_name":
UI_elements.text(layer, win, "shot_name",
x+85,
y+win.scroll["script_shots"]+current_Y_shots+5,
width-90,
40,
set_text=shotis)
# Now let's make it applyable. But first we need to filter
# the name. So there will not be any weird stuff. So the
# folder when it's created will not make problems.
newname = win.text["shot_name"]["text"].replace("/","_").replace(" ", "_")\
.replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
.replace("[","_").replace("]","_").replace("{","_").replace("}","_")
if newname not in shot_list:
def do():
win.current["key_letter"] = ""
shot_list[shotis][1] = newname
win.cur = "/"+scene+"/"+newname
# Saving to the file
story.save(win.project, win.story)
UI_elements.roundrect(layer, win,
width-45,
y+win.scroll["script_shots"]+current_Y_shots+5,
40,
40,
10,
button=do,
icon="ok")
else:
# A button to activate the editing I guess.
def do():
win.current["current_shot_name_editor"] = shotis
try:
del win.text["shot_name"]
except:
pass
win.textactive = "shot_name"
UI_elements.roundrect(layer, win,
x+85,
y+win.scroll["script_shots"]+current_Y_shots+5,
width-90,
40,
10,
button=do,
fill=False)
layer.stroke()
current_Y_shots = current_Y_shots + 60
# Let's make the color selectiong thing. Because I know people
# gonna eat me alive if the software does everything for them.
# Especially when it's something that customazible.
for num, col in enumerate(rcolors):
if win.story["shot_colors"][surl] == col:
# If the current color is the color of the shot then
# let's draw a little white thing around it.
UI_color.set(layer, win, "text_normal")
UI_elements.roundrect(layer, win,
x+(width/len(rcolors)*num),
y+win.scroll["script_shots"]+current_Y_shots-2,
width/len(rcolors),
10,
12)
UI_color.set(layer, win, col)
UI_elements.roundrect(layer, win,
x+(width/len(rcolors)*num)+2,
y+win.scroll["script_shots"]+current_Y_shots,
width/len(rcolors)-4,
10,
10)
# Also let's make a button that the user can click to change
# the color.
def do():
win.story["shot_colors"][surl] = col
UI_elements.roundrect(layer, win,
x+(width/len(rcolors)*num),
y+win.scroll["script_shots"]+current_Y_shots-2,
width/len(rcolors),
20,
12,
button=do,
fill=False)
layer.stroke()
current_Y_shots = current_Y_shots + 30
# Now let's draw 4 main folders of the shot.
# storyboard
# opengl
# test_rnd
# rendered
# There is also an extra folder created always.
# but since it's very technical let's not include it here.
# let's focus on those 4 because for the user these 4 will
# be our progress bar.
##### ##### ##### #####
########## ######### ########## ######## ##########
##### ##### ##### #####
# Like 4 links in a chain or something. I think it's a good
# representation.
fouricons = {
"storyboard":[],
"opengl":[],
"test_rnd":[],
"rendered":[]
}
fraction = 0.0
# Let's create a variable for the current selected folder.
if shotis+"_active_folder" not in win.current:
win.current[shotis+"_active_folder"] = "Pending"
for numb, icon in enumerate(fouricons):
# While we are drawing them. Let's find out whether there
# are any images inside. If yes. We add this to the over
# all percentage.
files = []
try:
files = sorted(os.listdir(win.project+"/rnd"+win.cur+"/"+icon))
except:
pass
fouricons[icon] = files
if files:
fraction = 0.25*(numb+1)
# If the icon is currently selected.
if win.current[shotis+"_active_folder"] == icon:
UI_color.set(layer, win, "progress_time")
UI_elements.roundrect(layer, win,
x+(width/5)*numb+(width/10)+20,
y+win.scroll["script_shots"]+current_Y_shots-2,
40,
40,
20)
def do():
win.current[shotis+"_active_folder"] = icon
UI_elements.roundrect(layer, win,
x+(width/5)*numb+(width/10)+20,
y+win.scroll["script_shots"]+current_Y_shots-2,
40,
40,
20,
button=do,
icon=icon,
tip=icon)
current_Y_shots = current_Y_shots + 50
# Let's find out which one is going to be selected.
if win.current[shotis+"_active_folder"] == "Pending":
if fouricons["rendered"]:
win.current[shotis+"_active_folder"] = "rendered"
elif fouricons["test_rnd"]:
win.current[shotis+"_active_folder"] = "test_rnd"
elif fouricons["opengl"]:
win.current[shotis+"_active_folder"] = "opengl"
else:
win.current[shotis+"_active_folder"] = "storyboard"
# Now let's draw a line that will act almost like a progress
# bar of a kind.
# It will be made of 4 circles stading on a line.
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+20,
y+win.scroll["script_shots"]+current_Y_shots-2,
width-40,
0,
5)
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
x+20,
y+win.scroll["script_shots"]+current_Y_shots-2,
(width-40)*fraction,
0,
5)
# Now the 4 circles.
for numb, icon in enumerate(fouricons):
UI_color.set(layer, win, "progress_background")
if fouricons[icon]: # If this folder has any files.
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
x+(width/5)*numb+(width/10)+30,
y+win.scroll["script_shots"]+current_Y_shots-7,
0,
0,
10)
current_Y_shots = current_Y_shots + 50
# The user might want to add the subfolders if they do not
# exist already.
at_least_one_missing = False
list_of_folders_to_generate = ["storyboard", "opengl", "test_rnd", "rendered", "extra"]
files_there = []
try:
files_there = os.listdir(win.project+"/rnd"+win.cur)
except:
pass
for i in list_of_folders_to_generate:
if i not in files_there:
at_least_one_missing = True
if at_least_one_missing:
def do():
for i in list_of_folders_to_generate:
try:
os.makedirs(win.project+"/rnd"+win.cur+"/"+i)
except:
pass
UI_elements.roundrect(layer, win,
10,
y+win.scroll["script_shots"]+current_Y_shots-10,
width-30,
40,
10,
button=do,
icon="folder",
tip=talk.text("GenerateSubfoldersTip"))
UI_color.set(layer, win, "text_normal")
layer.move_to(60, y+win.scroll["script_shots"]+current_Y_shots+15)
layer.show_text(talk.text("GenerateSubfolders"))
current_Y_shots = current_Y_shots + 50
# Now after we have the progress bar / selection type thingy.
# let's do a preview of the render. Usually. When rendering
# using Blender-Organizer legacy ( or blender console ) it makes
# files like 0001.png, 0002.png, 0003.png and so on. Now
# user might any type of stuff in that folder. And I don't want
# a broken program if stuff like this happens. So.
if shotis+"_active_folder_item" not in win.current:
win.current[shotis+"_active_folder_item"] = 0
# Making sure that we never going to select something beyond selectable.
if win.current[shotis+"_active_folder_item"] > len(fouricons[win.current[shotis+"_active_folder"]])-1:
win.current[shotis+"_active_folder_item"] = len(fouricons[win.current[shotis+"_active_folder"]])-1
# This will be the user selected file inside the folder.
# The frame. In case image fails to load.
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+5,
y+win.scroll["script_shots"]+current_Y_shots-7,
width-10,
200,
10,
fill=False)
layer.stroke()
# Image it self.
if fouricons[win.current[shotis+"_active_folder"]]:
imageurl = win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"]+"/"+fouricons[win.current[shotis+"_active_folder"]][win.current[shotis+"_active_folder_item"]]
UI_elements.image(layer, win,
imageurl,
x+10,
y+win.scroll["script_shots"]+current_Y_shots-2,
int(width-20),
int(190),
cell="shot_renders")
# Well how to live without a clickable openable thingy.
def do():
oscalls.file_open(win, imageurl)
UI_elements.roundrect(layer, win,
x+5,
y+win.scroll["script_shots"]+current_Y_shots-7,
width-10,
200,
10,
button=do,
fill=False)
layer.stroke()
else:
# If there is no image to show we can put one. Using a clipboard.
def do():
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
im = clipboard.wait_for_image()
if not im:
return
Px = im.get_width()
Py = im.get_height()
Pc = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
Pb = cairo.Context(Pc)
Gdk.cairo_set_source_pixbuf( Pb, im, 0, 0)
Pb.paint()
imageurl = win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"]+"/0001.png"
try:
os.makedirs(win.project+"/rnd"+win.cur+"/"+win.current[shotis+"_active_folder"])
except:
pass
Pc.write_to_png(imageurl)
UI_elements.roundrect(layer, win,
x+5+(width/2)-20,
y+win.scroll["script_shots"]+current_Y_shots-7+80,
40,
40,
10,
button=do,
icon="image_new")
current_Y_shots = current_Y_shots + 210
# After we drew our image. Let's draw a little timeline thing.
# so the user could select which frame from the animation they
# want to see. Basically a little bar. I guess. IDK. Like
# Blender's timeline I guess. But later I will draw a time
# graph on it. So it's not as simple as that.
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
x+5,
y+win.scroll["script_shots"]+current_Y_shots,
width-10,
100)
layer.fill()
numofis = len(fouricons[win.current[shotis+"_active_folder"]])
# Here as you can see I made the the rectangle a 100 pixels high
# this is for 1 simple reason. To be able to draw analytics.
# But.
# There are like one million problems with it. Let's go over them.
# * There are multiple blend files each one with analytics data.
# * Reading JSON files and drawing graphs is not a trivial thing
# requiring cashing of the image. Such as in Analytics window.
# Bull will see.
# * During rendeing the DATA changes. And I want to see the up
# to date data at all times.
# Let's try to implement it the simple way. Reading everything
# live. I've done that in Blender-Organizer. But the data was
# way simpler.
alldata = {}
largest = 1
# WAIT extra might not exists.
try:
for filename in os.listdir(win.project+"/rnd"+win.cur+"/extra"):
if filename.endswith(".blend.json"):
# I want to now open JSON FILE for each one and parse
# it. Actually when I think about it. It's not supposed
# to be that bad. But this computer has an SSD. LOL.
data = {}
try:
with open(win.project+"/rnd"+win.cur+"/extra/"+filename) as json_file:
data = json.load(json_file)
# Since I'm inside try I can do some wild stuff here.
data = data["analytics"][win.current[shotis+"_active_folder"]]
alldata[filename.replace(".json", "")] = data
for i in data:
if data[i] > largest:
largest = data[i]
except:
pass
except:
pass
# So let's now read through all the data that we have
for n, blend in enumerate(alldata):
data = alldata[blend]
# I want each new file to have it's own color. So
UI_color.set(layer, win, rcolors[n % len(rcolors)])
layer.move_to(
x+5,
y+win.scroll["script_shots"]+current_Y_shots+100
)
# Now let's draw everything.
for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
if str(numb+1) in data:
layer.line_to(
x+5+(width-10)/numofis*(numb+0.5),
(y+win.scroll["script_shots"]+current_Y_shots+100)-(100/largest*data[str(numb+1)])
)
layer.stroke()
# Funny that it didn't really stuck too much like this. Please
# let me know if on your machine this way of doing it is hell
# a slow.
for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
# Here I'm going to put some logic. That will enable me to
# scroll trough the images. In a way that allows me to
# preview the animation. Simple button wouldn't work since
# it's waiting for the user to release the button to be
# activated. This one will be activated by just dragging
# across.
# Also my roundrect buttons are not ideal when you have little
# space and 10000 frames.
fill = False
if int(win.current["mx"]) in range(int(x+5+(width-10)/numofis*numb),int(x+5+(width-10)/numofis*numb+(width-10)/numofis))\
and int(win.current["my"]) in range(int(y+win.scroll["script_shots"]+current_Y_shots), int(y+win.scroll["script_shots"]+current_Y_shots+100)):
# Before we are doing the logic. I want to give user a
# tooltip about render analytics. So let's do this.
tip = ""
for n, blend in enumerate(alldata):
data = alldata[blend]
if str(numb+1) in data:
tip = tip + blend+" "+UI_math.timestring(data[str(numb+1)]/1000000)+"\n"
if tip:
tip = tip[:-1] # Removing the last \n
UI_elements.tooltip(win, tip)
# Now let's do the fill and other stuff
fill = True
if win.current["LMB"]:
win.current[shotis+"_active_folder_item"] = numb
if fill or win.current[shotis+"_active_folder_item"] == numb:
UI_color.set(layer, win, "progress_time")
layer.rectangle(
x+5+(width-10)/numofis*numb,
y+win.scroll["script_shots"]+current_Y_shots,
(width-10)/numofis,
100)
layer.fill()
current_Y_shots = current_Y_shots + 110
# Okay let's put blend file from the shot. So you could actually
# do work on it.
# But first. I want to add a seach promt. So I could add new ones
# the same way I do it in any other window. By searching. I think
# it's important to keep the workflow similar.
# I think that if we have no blend files at all. It might give you
# to make a new one with the name of the shot. And all the rest will
# be done by searching. Yeah. Makes sense. Wait. I can put the name of
# the shot in the search. Hm... Okay. Let's make the seach icon first.
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/search.png",
x+10,
y+win.scroll["script_shots"]+current_Y_shots,
40,
40)
# Now beside it will be out text entry.
UI_elements.text(layer, win, "shot_search",
x+50,
y+win.scroll["script_shots"]+current_Y_shots,
width-55,
40)
current_Y_shots = current_Y_shots + 50
# Now let's get the blend file list. It's going to take a few
# stept. First we are going to try getting all the files in the
# folder. Then filter them by .blend in the end. We do not want
# to show you the .blend1 files. We don't want to show you
# anything.
blendfiles = []
try:
for blend in os.listdir(win.project+"/rnd"+win.cur):
if blend.endswith(".blend") and "_backup" not in blend:
blendfiles.append(blend)
except:
pass
# Now since we've got our blendfiles list. We can now draw them.
# But I want to preserve the countinuity with the rest of the
# software. So I will need to draw a little Blendfile node each
# time. For which I'm going to create a node. But this time it's
# going to have 2 icons at the bottom.
############## ############## ##############
# 01.blend # # 02.blend # # 03.blend #
############## ############## ##############
# ____ # # ____ # # ____ #
# ( 0 0 ) # # ( 0 0 ) # # ( 0 0 ) #
# ) ( # # ) ( # # ) ( #
# \__/ # # \__/ # # \__/ #
# # # # # #
############## ############## ##############
# 0 # 0 # # 0 # 0 # # 0 # 0 #
############## ############## ##############
# Those 2 icons in the bottom of each will be buttons for render
# and linking. The linking is what you do to put assets inside
# animation files. A new dialog should be developped for this.
# The rendering will resemble rendering of Blender-Organizer with
# render times analytics and stuff. But will be way more extended.
# For example with an ability to open a render task on another
# computer. So groups of people could work together.
tileX = 5
searchis = win.text["shot_search"]["text"].replace("/","_").replace(" ", "_")\
.replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
.replace("[","_").replace("]","_").replace("{","_").replace("}","_")
for bn, blend in enumerate(blendfiles):
# Each one will be drawn in it's own little layer. Because
# I want the top bar to have a nice rounded corner and also
# the image to be cut.
# Each one here will 128 pixels wide. ( Because this is the
# resolution of blender-thumbnailer.py thumbnails. And I
# want to save horizontal resolution as much as possible
# But before I render a Blend. I want to exclude it if it's
# not in a search.
if searchis and searchis.lower() not in blend.lower():
continue
# Making the layer
nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 198)
node = cairo.Context(nodesurface)
node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_elements.roundrect(node, win,
0,
0,
128,
198,
10,
fill=False)
node.clip()
# Background
UI_color.set(node, win, "dark_overdrop")
node.rectangle(0,0,128, 198)
node.fill()
# Blender icon
UI_elements.image(node, win,
win.project+"/rnd"+win.cur+"/"+blend,
0,
20,
128,
128, cell="shot_blends")
# Banner
UI_color.set(node, win, "node_blendfile")
node.rectangle(0,0,128, 20)
node.fill()
# Filename
UI_color.set(node, win, "text_normal")
node.set_font_size(12)
node.move_to(5, 15)
node.show_text(blend)
# Outputting the layer
layer.set_source_surface(nodesurface,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots)
layer.paint()
# Here we gonna out 3 buttons:
# 1. Launch the Blend-File
# 2. Link into Blend-File
# 3. Render Blend-File
# Launch the Blend file:
def do():
oscalls.file_open(win, win.project+"/rnd"+win.cur+"/"+blend)
UI_elements.roundrect(layer, win,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots,
128,
148,
10,
button=do,
fill=False,
tip=blend)
layer.stroke()
# Link things into Blend-File
def do():
# I'm using a dialog here. Because I want to return back
# to the shot we were in. But I don't really want to do
# anything after the dialog is closed. So there is this
# empty function.
def after(win, var):
pass
studio_dialogs.asset_link(win, "asset_linking", after, "/rnd"+win.cur+"/"+blend)
UI_elements.roundrect(layer, win,
x+tileX+20,
y+win.scroll["script_shots"]+current_Y_shots+152,
40,
40,
10,
icon="obj_link",
button=do,
tip=talk.text("link_shot_blend_tooltip")+blend)
# Render
def do():
def after(win, var):
pass
studio_dialogs.render(win, "render_setup", after, "/rnd"+win.cur+"/"+blend)
UI_elements.roundrect(layer, win,
x+tileX+65,
y+win.scroll["script_shots"]+current_Y_shots+152,
40,
40,
10,
icon="render",
button=do,
tip=talk.text("render_shot_blend_tooltip")+blend)
tileX = tileX + 150
if tileX + 128 > width - 10 and bn != len(blendfiles)-1:
tileX = 5
current_Y_shots = current_Y_shots + 205
# Now before we are finished. We want to able to add new files
# by typing the names in the search.
if not blendfiles and not searchis:
searchis = shotis
if not searchis.endswith(".blend"):
searchis = searchis + ".blend"
if searchis != ".blend" and searchis not in blendfiles:
# There will be 2 buttons. To add a brand new file and to
# copy one from a different shot. In Blender-Oraniger it
# had a copy-paste kind of system for this. But since we
# have a file selector. I'm going to use it instead. Tho
# tell me if a copy-paste for files is a cool idea.
if tileX + 128 > width - 10:
tileX = 5
current_Y_shots = current_Y_shots + 205
# ADD NEW BLEND
def do():
# First let's make sure that the folder exists. Because
# it might not exists. I think doing it automatically
# makes sense.
try:
os.makedirs(win.project+"/rnd"+win.cur)
except:
pass
# Then we copy the file.
oscalls.copy_file(
win,
os.getcwd()+"/new_file/rnd.blend",
"/rnd"+win.cur+"/",
searchis)
# And clear the search
win.text["shot_search"]["text"] = ""
win.images = {}
UI_elements.roundrect(layer, win,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots,
128,
198,
10,
button=do)
# A little surrounding thing.
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots,
128,
198,
10,
fill=False)
layer.stroke()
# Icon
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/new_file.png",
x+tileX+44,
y+win.scroll["script_shots"]+current_Y_shots + 70,
40, 40)
# preiew
UI_color.set(layer, win, "text_normal")
layer.set_font_size(12)
layer.move_to(x+tileX+64-len(searchis)*4,
y+win.scroll["script_shots"]+current_Y_shots+150)
layer.show_text(searchis)
tileX = tileX + 150
if tileX + 128 > width - 10:
tileX = 5
current_Y_shots = current_Y_shots + 205
# And a copy file here too
# COPY BLEND
def do():
# Now the copy function going to be a tiny bit harder
# since we will need to launch a dialog for you to
# select the file. I think you gonna look for files
# from this scene more often. So let's do that.
def after(win, var):
if var:
try:
os.makedirs(win.project+"/rnd"+win.cur)
except:
pass
# Then we copy the file.
oscalls.copy_file(
win,var,
"/rnd"+win.cur+"/",
searchis)
# And clear the search
win.text["shot_search"]["text"] = ""
win.images = {}
# Running the dialog starter
studio_dialogs.file_select(win, shotis+"_blends", after, force=True,
IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=False, VEH=False,
LOC=False, OBJ=False, RND=True, FOLDER=False, SEARCH=scene+" ")
UI_elements.roundrect(layer, win,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots,
128,
198,
10,
button=do)
# A little surrounding thing.
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x+tileX,
y+win.scroll["script_shots"]+current_Y_shots,
128,
198,
10,
fill=False)
layer.stroke()
# Icon
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/copy_file.png",
x+tileX+44,
y+win.scroll["script_shots"]+current_Y_shots + 70,
40, 40)
# preiew
UI_color.set(layer, win, "text_normal")
layer.set_font_size(12)
layer.move_to(x+tileX+64-len(searchis)*4,
y+win.scroll["script_shots"]+current_Y_shots+150)
layer.show_text(searchis)
current_Y_shots = current_Y_shots + 205
else:
# If the shot is not selected I want to be able to select it
# even if I can't find it in the text of the script.
def do():
win.cur = surl
win.current["scroll_shot_to_in_script"] = True
win.current["shot_left_side_scroll_please_work_omg_wtf"] = True
UI_elements.roundrect(layer, win,
x,
y+win.scroll["script_shots"]+current_Y_shots,
width,
50,
10,
button=do,
fill=False)
layer.stroke()
# Then I want to show the user which state is the shot at the
# moment. By putting one of it's icons in the end of the of the
# shot choosing button.
fouricons = [
"storyboard",
"opengl",
"test_rnd",
"rendered"
]
for icon in reversed(fouricons):
try:
files = sorted(os.listdir(win.project+"/rnd"+surl+"/"+icon))
except:
files = []
if files:
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
width-50, y+win.scroll["script_shots"] + current_Y_shots+5, 40, 40)
break
current_Y_shots = current_Y_shots + 60
# Button for [ That's All The Shots ].
# The idea is that untill this button is pressed the program will try
# to estimate how many shots there are still in the text, by looking at
# the amount of the text. If this button is pressed. The estimation will
# be ignored.
if "no_more_shots" not in win.story["scenes"][scene]:
win.story["scenes"][scene]["no_more_shots"] = False
icon = "checked"
if not win.story["scenes"][scene]["no_more_shots"]:
icon = "unchecked"
def do():
win.story["scenes"][scene]["no_more_shots"] = not win.story["scenes"][scene]["no_more_shots"]
story.save(win.project, win.story)
win.story = story.load(win.project)
UI_elements.roundrect(layer, win,
x,
y+win.scroll["script_shots"]+current_Y_shots,
width,
40,
10,
button=do,
icon=icon)
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+50,
y+win.scroll["script_shots"]+current_Y_shots+27 )
layer.show_text("No More Shots")
current_Y_shots = current_Y_shots + 60
# Scroll
UI_elements.scroll_area(layer, win, "script_shots",
x+0,
y+0,
width,
height,
current_Y_shots,
bar=True,
mmb=True)
return surface