Jeison Yehuda Amihud (Blender Dumbass)
162d3f8038
Implemented missing features that could be seen as bugs for the casual users. Such as sync of the main checklist. And other minor changes.
1141 lines
40 KiB
Python
1141 lines
40 KiB
Python
# THIS FILE IS A PART OF VCStudio
|
|
# PYTHON 3
|
|
|
|
import os
|
|
import cairo
|
|
|
|
#UI modules
|
|
from UI import UI_elements
|
|
from UI import UI_color
|
|
|
|
#settings
|
|
from settings import talk
|
|
|
|
#studio
|
|
from studio import analytics
|
|
from studio import history
|
|
|
|
def get_list(filepath):
|
|
|
|
# This fucntion converts text documents. (.progress) into a more machine
|
|
# friendly recursive dict.
|
|
|
|
# In the original organizer evaluation of the checklist was done separatelly
|
|
# in a different function. Which made it very messy for recursive stuff.
|
|
# I will attemp to combine it. And make it more clean as a result.
|
|
|
|
checklist = {
|
|
"fraction":0.0, # The percentage of a checklist. From 0.0 to 1.0
|
|
"string":filepath,
|
|
"editing":False,# Whether the string is during editing. UI.
|
|
"selected":False,# Whether the current task is selected.
|
|
"open":True, # Whether to draw the suptasks. UI.
|
|
"subtasks":[] # List of subtastks. (In the same format as the checklist)
|
|
}
|
|
|
|
|
|
data = open(filepath)
|
|
data = data.read()
|
|
data = data.split("\n")
|
|
|
|
# Let's filter out all the comments. Lines starting with #. For some reason
|
|
# in the old organizer. I just thought it wasn't important. LOL. And just
|
|
# started reading from the 10th line.
|
|
|
|
# Here is an example of the first 9 lines.
|
|
|
|
# 1 #### Blender orgainizer checklist format
|
|
# 2 #### INDINTATION (4 SPACES LONG)
|
|
# 3 #### STR means Start date of the ASSET
|
|
# 4 #### FIN means Finish deadline of the asset
|
|
# 5 #### [ ] means that task is on list
|
|
# 6 #### [V] means that tast is finished
|
|
# 7 #### DO NOT USE EMPTY LINES
|
|
# 8 STR 24/02/2020
|
|
# 9 FIN 01/05/2021
|
|
|
|
# You can see a trace from a very long time ago. From the first versions
|
|
# of the blender organizer. The STR and FIN values. Which are start and
|
|
# deadline of the project.
|
|
|
|
# I guess we need to filter it out a bit differently. Checking whether a
|
|
# given line starts with a [ or with a number of spaces and [. This will
|
|
# make the file a little more open to editing by hand. Without too much
|
|
# worrying that something will break.
|
|
|
|
cleandata = []
|
|
|
|
for line in data:
|
|
|
|
# So not to mangle the line.
|
|
tmp = line
|
|
while tmp.startswith(" "):
|
|
tmp = tmp[1:]
|
|
|
|
# Checking
|
|
if tmp.startswith("[ ]") or tmp.startswith("[V]"):
|
|
cleandata.append(line)
|
|
|
|
# Now since we have the cleandata. We can try to parse it somehow into a
|
|
# checklist thing. For this we need a reqursion. I gonna use a method from
|
|
# the blender-organizer. By running the function with in itself. It's not
|
|
# very wise. I know. In python3 it will give you no more then 996 recursions
|
|
# untill it will declare an error. BUT. It's 996 layers of a subtaks.
|
|
# For now I don't see a need in so many subtasks. Let's think of layers of
|
|
# subtasks as of memory. This laptop has only 8 GB of RAM. I can't put more
|
|
# data into it even if I wanted.
|
|
|
|
def convert(part, indent=0):
|
|
# If a thing have subtasks it should not even be a checkbox. So trying
|
|
# To evaluate it's fraction by whether it's a [ ] or [V] shoun't be
|
|
# done.
|
|
|
|
subtask = []
|
|
|
|
for num, line in enumerate(part):
|
|
|
|
# Let's get the NEXT line. I'm not kidding. We gonna work with the
|
|
# next line to see whether to count this lines [V]
|
|
|
|
|
|
if line[indent:].startswith("["):
|
|
|
|
thisline = {
|
|
"fraction":0.0,
|
|
"string":line[line.find("]")+2:],
|
|
"editing":False,
|
|
"selected":False,
|
|
"open":False,
|
|
"subtasks":[]
|
|
}
|
|
|
|
try:
|
|
nextline = part[num+1]
|
|
except:
|
|
nextline = ""
|
|
|
|
if not line[line.find("]")+1] == ".":
|
|
thisline["open"] = True
|
|
|
|
if nextline.find("[")-1 <= indent:
|
|
if line[indent:].startswith("[V]"):
|
|
thisline["fraction"] = 1.0
|
|
|
|
else:
|
|
subpart = []
|
|
subdent = indent
|
|
for n, l in enumerate(part[num+1:]):
|
|
if n == 0:
|
|
subdent = l.find("[")
|
|
|
|
if l.find("[")-1 <= indent:
|
|
break
|
|
|
|
else:
|
|
subpart.append(l)
|
|
|
|
#print(subpart)
|
|
thisline["subtasks"] = convert(subpart, subdent)
|
|
|
|
fracs = []
|
|
for task in thisline["subtasks"]:
|
|
if not task["string"].startswith("#"):
|
|
fracs.append(task["fraction"])
|
|
|
|
try:
|
|
thisline["fraction"] = sum(fracs) / len(fracs)
|
|
except:
|
|
thisline["fraction"] = 0.0
|
|
|
|
# Sometime it was showing 99% when infect it's 100%
|
|
if thisline["fraction"] == 0.9999:
|
|
thisline["fraction"] = 1.0
|
|
|
|
|
|
subtask.append(thisline)
|
|
|
|
return subtask
|
|
|
|
|
|
checklist["subtasks"] = convert(cleandata)
|
|
fracs = []
|
|
for task in checklist["subtasks"]:
|
|
if not task["string"].startswith("#"):
|
|
fracs.append(task["fraction"])
|
|
|
|
try:
|
|
checklist["fraction"] = sum(fracs) / len(fracs)
|
|
except:
|
|
checklist["fraction"] = 0.0
|
|
|
|
# Sometime it was showing 99% when infect it's 100%
|
|
if checklist["fraction"] == 0.9999:
|
|
checklist["fraction"] = 1.0
|
|
|
|
|
|
return checklist
|
|
|
|
def get_fraction(win, path):
|
|
|
|
############################################################################
|
|
|
|
# This function will return a fraction of a given checklist. It will ignore
|
|
# complitelly what is the asset / shot / project the checklist is from.
|
|
|
|
# For sake of making this function actually somewhat useful, aka not bloated
|
|
# I will use it to cashe the whole checklist data objects. So they could be
|
|
# easily accesable later on.
|
|
|
|
# This is usefull so I would not need to create 2 cash data structures. One
|
|
# for fractions and for the checklists them selves.
|
|
|
|
############################################################################
|
|
|
|
|
|
if path not in win.checklists:
|
|
|
|
# Let's check if path exists in the project first.
|
|
|
|
if os.path.exists(win.project+"/"+path):
|
|
win.checklists[path] = get_list(win.project+"/"+path)
|
|
else:
|
|
win.checklists[path] = get_list(path)
|
|
|
|
|
|
# Let's now return back the fraction
|
|
|
|
return win.checklists[path]["fraction"]
|
|
|
|
def get_task_by_path(tasks, path, p=[]):
|
|
|
|
############################################################################
|
|
|
|
# This function will give the information about a given task from a non
|
|
# recursive URL such as ["Task", "Sub-task", "Sub-task2"]. This will look
|
|
# the recursive list and output the given task. In this case "Sub-task2"
|
|
# with all the information inside it.
|
|
|
|
############################################################################
|
|
|
|
|
|
for task in tasks:
|
|
|
|
pa = p.copy()
|
|
pa.append(" "+task["string"])
|
|
|
|
|
|
if pa == path:
|
|
return task
|
|
|
|
if task["subtasks"]:
|
|
t = get_task_by_path(task["subtasks"], path, pa)
|
|
if t:
|
|
return t
|
|
|
|
return False
|
|
|
|
def filter_tasks(data):
|
|
|
|
############################################################################
|
|
|
|
# This function going to remove all selections and all edits from a given
|
|
# checklist. This is nessesary for being able to select one task without
|
|
# manually deselcting the other. And since the checklist is recursive it's
|
|
# not a trivial task.
|
|
|
|
############################################################################
|
|
|
|
data["selected"] = False
|
|
data["editing"] = False
|
|
|
|
if data["subtasks"]:
|
|
for i in data["subtasks"]:
|
|
filter_tasks(i)
|
|
|
|
|
|
def save(path, data):
|
|
|
|
############################################################################
|
|
|
|
# This funtion will save the checklist into a .progress file. Formatted
|
|
# similarly as in the old Blender-Organizer. But as easy to understand.
|
|
#
|
|
# NOTE: I will remove some stuff from the file. Mainly the comments in the
|
|
# beginning and the STR and END data. Which are no longer needed in a
|
|
# VCStudio project.
|
|
#
|
|
# But since some part of Legacy Organizer relies on those, it's not adviced
|
|
# to open the projects that you are planning to work on with Blender-Organizer
|
|
# in VCStudio.
|
|
|
|
############################################################################
|
|
|
|
|
|
indent = [0]
|
|
|
|
lines = []
|
|
|
|
def writetasks(tasks):
|
|
|
|
for task in tasks:
|
|
if task["fraction"] and not task["subtasks"]:
|
|
v = "[V]"
|
|
else:
|
|
v = "[ ]"
|
|
|
|
if task["open"]:
|
|
o = " "
|
|
else:
|
|
o = "."
|
|
|
|
lines.append(" "*indent[0]+v+o+task["string"])
|
|
|
|
if task["subtasks"]:
|
|
indent[0] = indent[0] + 4
|
|
writetasks(task["subtasks"])
|
|
indent[0] = indent[0] - 4
|
|
|
|
writetasks(data["subtasks"])
|
|
|
|
# Writting to file
|
|
w = open(path, "w")
|
|
|
|
for i in lines:
|
|
w.write(i+"\n")
|
|
|
|
w.close()
|
|
|
|
def draw(outlayer, win, path, back="story_editor"):
|
|
|
|
############################################################################
|
|
|
|
# This function will draw the checklists to the screen.
|
|
|
|
# You probably noticed that there is no x, y, width or height values that
|
|
# you can input. This due to one idea that I have in mind.
|
|
|
|
# I'm planning to make scheduling a kind of interactive process rather then
|
|
# a boring date selection. Something that's going to be somewhat fun to do.
|
|
|
|
# The idea is to grab the task and to drug it outside the checklist. Which
|
|
# will call for an analytics window to show up, where you can put the
|
|
# schedules into a given date.
|
|
|
|
# For this reason the checklist should always be in the same spot on the
|
|
# screen. Which is a (window width) / 4 at the right side.
|
|
|
|
############################################################################
|
|
|
|
if path not in win.checklists:
|
|
|
|
# Let's check if path exists in the project first.
|
|
|
|
if os.path.exists(win.project+"/"+path):
|
|
win.checklists[path] = get_list(win.project+"/"+path)
|
|
elif os.path.exists(win.project+"/rnd"+path):
|
|
win.checklists[path] = get_list(win.project+"/rnd"+path)
|
|
else:
|
|
win.checklists[path] = get_list(path)
|
|
|
|
|
|
x = win.current["w"] / 4 * 3 + 10
|
|
y = 10
|
|
width = win.current["w"] / 4 - 20
|
|
height = win.current["h"] - 20
|
|
|
|
# Now let's make a layer.
|
|
|
|
# Making the layer
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
|
|
layer = cairo.Context(surface)
|
|
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
|
|
|
# Clip
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
0,
|
|
width,
|
|
height,
|
|
10,
|
|
fill=False)
|
|
layer.clip()
|
|
|
|
|
|
|
|
# Checklist icon.
|
|
UI_color.set(layer, win, "node_background")
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
0,
|
|
width,
|
|
50,
|
|
10)
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/checklist.png",
|
|
5, 5, 40, 40)
|
|
|
|
# Fraction
|
|
|
|
fraction = win.checklists[path]["fraction"]
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
50,
|
|
17,
|
|
width - 60,
|
|
0,
|
|
7)
|
|
|
|
UI_color.set(layer, win, "progress_active")
|
|
UI_elements.roundrect(layer, win,
|
|
50,
|
|
17,
|
|
(width - 60 )*fraction,
|
|
0,
|
|
7)
|
|
|
|
# Clip
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
60,
|
|
width,
|
|
height,
|
|
10,
|
|
fill=False)
|
|
layer.clip()
|
|
|
|
|
|
###########################################################################
|
|
|
|
# NOW THIS IS THE HARD RECURSION PART! THERE IS GOING TO BE SOME REDIC!
|
|
|
|
###########################################################################
|
|
|
|
tileX = 0
|
|
current_Y = 70
|
|
|
|
# There is some bullshit regarding the Global variables. I gonna do a bit
|
|
# trickier. Since lists are only links to lists. Let's do this.
|
|
|
|
cXY = [tileX, current_Y]
|
|
|
|
if "checklist" not in win.scroll:
|
|
win.scroll["checklist"] = 0
|
|
|
|
if "moving_task_now" not in win.current:
|
|
win.current["moving_task_now"] = False
|
|
|
|
def draw_tasks(tasks, cXY, schedulep):
|
|
|
|
|
|
|
|
#########################################
|
|
# #
|
|
# THIS IS THE RECURSIVE FUNCTION #
|
|
# #
|
|
#########################################
|
|
|
|
# I will try to explain what is going on here. But it will require me
|
|
# understanding the code myself. And it's a bit redic. There was a hell
|
|
# of a lot of copy-paste, edit value, continue.
|
|
|
|
for num , task in enumerate(tasks):
|
|
|
|
# This is the code that will be done for each task in the list. Or
|
|
# subtask if a task have them.
|
|
|
|
# Let's get a schedule path. A folder like structure.
|
|
# Exacmple:
|
|
|
|
# Instead of:
|
|
# Task
|
|
# Sub-Task
|
|
# Sub-Task 2
|
|
|
|
# We get:
|
|
# [" Task", " Sub-Task", " Sub-Task 2"]
|
|
|
|
schedulepath = schedulep.copy()
|
|
schedulepath.append(" "+task["string"])
|
|
|
|
|
|
###### SCHEDULING STUFF BACKWARD ######
|
|
|
|
# This is a set of stuff to make schedules work with checklists.
|
|
|
|
if "schedule_task_selected" not in win.current:
|
|
win.current["schedule_task_selected"] = False
|
|
|
|
if win.current["schedule_task_selected"] and win.cur == win.current["schedule_task_selected"][-1]\
|
|
or win.current["schedule_task_selected"] and win.cur == "/set" and win.current["schedule_task_selected"][-1] == "":
|
|
csl = win.current["schedule_task_selected"][0][0][4]
|
|
|
|
|
|
if " "+task["string"] in csl and not task["open"]:
|
|
task["open"] = True
|
|
|
|
if schedulepath == csl and not task["selected"]:
|
|
filter_tasks(win.checklists[path])
|
|
task["selected"] = True
|
|
win.scroll["checklist"] = 0 - cXY[1] + height/2
|
|
|
|
|
|
#### DELETE SHORT KEY ####
|
|
|
|
# There is probably a reason to put it here. Probably something
|
|
# breaks if you put it anywhere else. IDK actually. Test it. I
|
|
# don't remember
|
|
|
|
if 65535 in win.current["keys"] and task["selected"] and not win.current["schedule_task_selected"]:
|
|
del tasks[num]
|
|
win.current["keys"] = []
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.analytics = analytics.load(win.project)
|
|
win.multiuser["last_request"] = ""
|
|
|
|
#### THE GRABBING FUNCTION ####
|
|
|
|
# This is the activation of the grab tool. It uses the same
|
|
# win.current["tool"] = "grab" as the story editor. Because it makes
|
|
# sense to reuse it if it already exists. For sake of conviniense.
|
|
|
|
# It doesn't mean that it uses the same code. It's a bit different.
|
|
# due to the nature of lists vs free positioning items.
|
|
|
|
# Now let's set up a few positional variables. So I could move the tasks
|
|
# while moving the currently selected task. And in the same time will not
|
|
# screw the data.
|
|
|
|
# So I copy the X and the Y locations like this.
|
|
|
|
sx = cXY[0]
|
|
sy = win.scroll["checklist"] + cXY[1]
|
|
|
|
grabY = 40
|
|
if task["subtasks"]:
|
|
grabY = 60
|
|
|
|
# Grab
|
|
if win.current["LMB"]\
|
|
and int(win.current["LMB"][0]) in range(int(x+sx), int(x+sx+width))\
|
|
and int(win.current["LMB"][1]) in range(int(y+sy), int(y+sy+grabY))\
|
|
and win.current["tool"] == "selection"\
|
|
and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-2), int(win.current["mx"]+2))\
|
|
and int(win.current["LMB"][1]) not in range(int(win.current["my"]-2), int(win.current["my"]+2)):
|
|
|
|
# So here we set up the grab tool and creating a little variable.
|
|
# This variable (moving_task_now) will consist of 2 parts on release.
|
|
|
|
# 1. The entire task ( with subtasks ) using pop. Which is not simply
|
|
# a copy. But also removing the task from the checklist's list.
|
|
|
|
# 2. The current frame of poping. So to offset insertion for 1 frame.
|
|
# This insures that you insert the task in the correct spot.
|
|
|
|
filter_tasks(win.checklists[path])
|
|
task["selected"] = True
|
|
win.current["schedule_task_selected"] = False
|
|
|
|
win.current["tool"] = "grab"
|
|
win.current["moving_task_now"] = False
|
|
|
|
# Now let's actually setup the pop. So when the mouse is now pressed, but
|
|
# was on a previous framae, and our current tool is grab.
|
|
|
|
if not win.current["LMB"] and win.previous["LMB"] and win.current["tool"] == "grab"\
|
|
and task["selected"]:
|
|
|
|
# We remove the task from the list. And write it to the variable. With
|
|
# the current frame.
|
|
|
|
win.current["moving_task_now"] = [tasks.pop(num), win.current["frame"]]
|
|
|
|
|
|
|
|
# Now I can touch the sx and sy without screwing up the cXY or scroll.
|
|
|
|
thismoving = False # This is going to be True is this is the task that's
|
|
# moving currently
|
|
somemoving = False # This is going to be True if any task is moving
|
|
someXY = [0,0] # And this is the mouse position translated to location
|
|
# of the checklist. (x, y coordinates of the whole
|
|
# widget). Or in other words position of the task.
|
|
|
|
# Then comes some logic to determen those 3 values.
|
|
|
|
if win.current["tool"] == "grab":
|
|
|
|
# Okay so here. If grab first we assume that it's some other
|
|
# task. And now currently selected one.
|
|
|
|
somemoving = True
|
|
someXY = [
|
|
win.current["mx"]-x,
|
|
win.current["my"]-y
|
|
]
|
|
|
|
# Now this is the editing of the sx and sy values. To be at
|
|
# the position of the mouse.
|
|
|
|
if task["selected"]:
|
|
|
|
# Calculating so the mouse will end up about in the centre
|
|
# of the task while the task is at motion.
|
|
|
|
sx = win.current["mx"] - x - (width - cXY[0])/2
|
|
sy = win.current["my"] - y - 10
|
|
task["open"] = False
|
|
thismoving = True
|
|
somemoving = False
|
|
|
|
# Now if the mouse is outside the frame of the checklist
|
|
# it will activate the scheduling.
|
|
|
|
############################################################
|
|
# SCHEDULING PART #
|
|
############################################################
|
|
|
|
if win.current["mx"] < x:
|
|
win.url = "analytics"
|
|
win.current["grab_data"] = [path, back, win.cur, schedulepath, win.settings["Username"]]
|
|
win.current["tool"] = "schedule"
|
|
#
|
|
#
|
|
#
|
|
# SEE studio/studio_analyticsLayer.py
|
|
# for more details about scheduling
|
|
#
|
|
|
|
# And if back into frame it will return to normal grab mode.
|
|
|
|
elif win.current["tool"] == "schedule" and win.current["mx"] > x:
|
|
win.url = back
|
|
win.current["tool"] = "grab"
|
|
|
|
####################################################################
|
|
# SCHEDULING PART END #
|
|
####################################################################
|
|
|
|
|
|
inside = False
|
|
between = False
|
|
|
|
if sy-10 < someXY[1] < sy+10 and somemoving and not task["selected"]:
|
|
|
|
if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
|
|
tasks.insert(num, win.current["moving_task_now"][0])
|
|
win.current["tool"] = "selection"
|
|
win.current["LMB"] = False
|
|
win.previous["LMB"] = False
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.analytics = analytics.load(win.project)
|
|
win.multiuser["last_request"] = ""
|
|
|
|
elif win.current["LMB"]:
|
|
|
|
for line in range(int(cXY[0]/20)):
|
|
|
|
line = line * 20 + 10
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
|
|
layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
|
|
layer.stroke()
|
|
|
|
cXY[1] = cXY[1] + 50
|
|
|
|
sx = cXY[0]
|
|
sy = win.scroll["checklist"] + cXY[1]
|
|
|
|
between = True
|
|
somemoving = False
|
|
|
|
elif sy < someXY[1] < sy + 40 and somemoving and not task["selected"]:
|
|
|
|
if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
|
|
task["subtasks"].append(win.current["moving_task_now"][0])
|
|
win.current["tool"] = "selection"
|
|
win.current["LMB"] = False
|
|
win.previous["LMB"] = False
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.analytics = analytics.load(win.project)
|
|
win.multiuser["last_request"] = ""
|
|
|
|
elif win.current["LMB"]:
|
|
inside = True
|
|
|
|
# Selection
|
|
if win.textactive != "editing_task" and win.current["tool"] == "selection":
|
|
def do():
|
|
ed = task["selected"] and not task["editing"]
|
|
filter_tasks(win.checklists[path])
|
|
win.current["schedule_task_selected"] = False
|
|
task["selected"] = True
|
|
if ed:
|
|
task["editing"] = True
|
|
UI_elements.roundrect(layer, win,
|
|
sx+40,
|
|
sy,
|
|
width - cXY[0]-40,
|
|
grabY,
|
|
10,
|
|
button=do,
|
|
offset=[x,y],
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
# Background
|
|
if not task["subtasks"]:
|
|
UI_color.set(layer, win, "node_background")
|
|
if task["string"].startswith("#"):
|
|
UI_color.set(layer, win, "dark_overdrop")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
40,
|
|
10)
|
|
|
|
if inside:
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
if task["selected"]:
|
|
UI_color.set(layer, win, "text_normal")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
# Line to see how deep you are in
|
|
|
|
for line in range(int(cXY[0]/20)):
|
|
|
|
line = line * 20 + 10
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
|
|
layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
|
|
layer.stroke()
|
|
|
|
if not task["string"].startswith("#"):
|
|
if task["fraction"]:
|
|
im = "checked"
|
|
else:
|
|
im = "unchecked"
|
|
|
|
# CHECK BUTTON
|
|
|
|
def do():
|
|
if task["fraction"]:
|
|
task["fraction"] = 0.0
|
|
history.record(win, path, "[Un-Checked]", schedulepath)
|
|
else:
|
|
task["fraction"] = 1.0
|
|
history.record(win, path, "[Checked]", schedulepath)
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.multiuser["last_request"] = ""
|
|
win.analytics = analytics.load(win.project)
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon=im,
|
|
offset=[x,y])
|
|
|
|
|
|
else:
|
|
UI_color.set(layer, win, "node_background")
|
|
if task["string"].startswith("#"):
|
|
UI_color.set(layer, win, "dark_overdrop")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
60,
|
|
10)
|
|
|
|
if inside:
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
60,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
|
|
if task["selected"]:
|
|
UI_color.set(layer, win, "text_normal")
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
width - cXY[0],
|
|
60,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
# Line to see how deep you are in
|
|
|
|
for line in range(int(cXY[0]/20)):
|
|
|
|
line = line * 20 + 10
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
|
|
layer.line_to(line, win.scroll["checklist"] + cXY[1]+60)
|
|
layer.stroke()
|
|
|
|
# Fraction
|
|
if not task["string"].startswith("#"):
|
|
fraction = task["fraction"]
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
sx+10,
|
|
sy+45,
|
|
width-20 - cXY[0],
|
|
0,
|
|
5)
|
|
|
|
UI_color.set(layer, win, "progress_active")
|
|
UI_elements.roundrect(layer, win,
|
|
sx+10,
|
|
sy+45,
|
|
(width -20 - cXY[0])*fraction,
|
|
0,
|
|
5)
|
|
|
|
if task["open"]:
|
|
im = "open"
|
|
else:
|
|
im = "closed"
|
|
|
|
def do():
|
|
task["open"] = not task["open"]
|
|
win.current["schedule_task_selected"] = False
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.multiuser["last_request"] = ""
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
sx,
|
|
sy,
|
|
40,
|
|
60,
|
|
10,
|
|
button=do,
|
|
icon=im,
|
|
offset=[x,y])
|
|
|
|
|
|
|
|
|
|
# TEXT
|
|
|
|
# ECS
|
|
if 65307 in win.current["keys"] and task["editing"]:
|
|
|
|
# It's here because Text entry has it's own ESC
|
|
|
|
win.textactive = ""
|
|
task["editing"] = False
|
|
del win.text["editing_task"]
|
|
win.current["keys"] = []
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.analytics = analytics.load(win.project)
|
|
win.multiuser["last_request"] = ""
|
|
|
|
if not task["editing"]:
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
sx+50,
|
|
sy+25
|
|
)
|
|
if task["string"].startswith("#"):
|
|
UI_color.set(layer, win, "progress_background")
|
|
if not task["subtasks"]:
|
|
layer.move_to(
|
|
sx+10,
|
|
sy+25
|
|
)
|
|
layer.show_text(task["string"][1:])
|
|
else:
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.show_text(task["string"])
|
|
else:
|
|
|
|
|
|
UI_elements.text(layer, win, "editing_task",
|
|
sx+39,
|
|
sy,
|
|
width - cXY[0] - 40,
|
|
40,
|
|
set_text=task["string"],
|
|
offset=[x,y])
|
|
|
|
win.textactive = "editing_task"
|
|
|
|
# Assigning the text
|
|
|
|
|
|
def do():
|
|
task["string"] = win.text["editing_task"]["text"]
|
|
win.textactive = ""
|
|
filter_tasks(win.checklists[path])
|
|
del win.text["editing_task"]
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.multiuser["last_request"] = ""
|
|
|
|
def button():
|
|
do()
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
sx+39 + width - cXY[0] - 80,
|
|
sy,
|
|
40,
|
|
40,
|
|
10,
|
|
button=button,
|
|
icon="ok",
|
|
tip=talk.text("checked"),
|
|
offset=[x,y])
|
|
|
|
# Enter
|
|
if 65293 in win.current["keys"]:
|
|
button()
|
|
win.current["keys"] = []
|
|
|
|
|
|
# Tab
|
|
if 65289 in win.current["keys"]:
|
|
do()
|
|
tasks.append(
|
|
{
|
|
"fraction":0.0,
|
|
"string":"",
|
|
"editing":True,
|
|
"selected":True,
|
|
"open":False,
|
|
"subtasks":[]
|
|
}
|
|
)
|
|
win.current["keys"] = []
|
|
|
|
# RECURSION
|
|
if not thismoving:
|
|
if not task["subtasks"]:
|
|
cXY[1] = cXY[1] + 50
|
|
else:
|
|
cXY[1] = cXY[1] + 70
|
|
|
|
cXY[0] = cXY[0] + 20
|
|
|
|
# THERE IS YOUR RECURSION
|
|
|
|
if task["open"]:
|
|
draw_tasks(task["subtasks"], cXY, schedulepath)
|
|
|
|
cXY[0] = cXY[0] - 20
|
|
|
|
# Adding subtasks
|
|
|
|
if ((task["subtasks"] and task["open"] and task["selected"])\
|
|
or (task["selected"] and not task["subtasks"]))\
|
|
and win.textactive != "editing_task"\
|
|
and win.current["tool"] == "selection":
|
|
|
|
cXY[0] = cXY[0] + 20
|
|
|
|
def do():
|
|
task["open"] = True
|
|
filter_tasks(win.checklists[path])
|
|
task["subtasks"].append(
|
|
{
|
|
"fraction":0.0,
|
|
"string":"",
|
|
"editing":True,
|
|
"selected":True,
|
|
"open":False,
|
|
"subtasks":[]
|
|
}
|
|
)
|
|
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
cXY[0],
|
|
win.scroll["checklist"] + cXY[1],
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="new",
|
|
offset=[x,y])
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
cXY[0],
|
|
win.scroll["checklist"] + cXY[1],
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
cXY[0]+50,
|
|
win.scroll["checklist"] + cXY[1]+25
|
|
)
|
|
layer.show_text(talk.text("add_new_subtask"))
|
|
|
|
for line in range(int(cXY[0]/20)):
|
|
|
|
line = line * 20 + 10
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
layer.move_to(line, win.scroll["checklist"] + cXY[1]-10)
|
|
layer.line_to(line, win.scroll["checklist"] + cXY[1]+40)
|
|
layer.stroke()
|
|
|
|
|
|
cXY[0] = cXY[0] - 20
|
|
cXY[1] = cXY[1] + 50
|
|
|
|
schedulepath = []
|
|
draw_tasks(win.checklists[path]["subtasks"], cXY, schedulepath)
|
|
|
|
# Go to the bottom.
|
|
|
|
if win.current["tool"] == "grab"\
|
|
and win.current["my"] - y > win.scroll["checklist"] + cXY[1]:
|
|
|
|
if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]:
|
|
win.checklists[path]["subtasks"].append(win.current["moving_task_now"][0])
|
|
win.current["tool"] = "selection"
|
|
win.current["LMB"] = False
|
|
win.previous["LMB"] = False
|
|
|
|
# Saving
|
|
save(path, win.checklists[path])
|
|
win.checklists = {}
|
|
win.assets = {}
|
|
win.analytics = analytics.load(win.project)
|
|
win.multiuser["last_request"] = ""
|
|
|
|
|
|
|
|
# Adding a task.
|
|
if win.textactive != "editing_task"\
|
|
and win.current["tool"] == "selection":
|
|
def do():
|
|
filter_tasks(win.checklists[path])
|
|
win.checklists[path]["subtasks"].append(
|
|
{
|
|
"fraction":0.0,
|
|
"string":"",
|
|
"editing":True,
|
|
"selected":True,
|
|
"open":False,
|
|
"subtasks":[]
|
|
}
|
|
)
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
cXY[0],
|
|
win.scroll["checklist"] + cXY[1],
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="new",
|
|
offset=[x,y])
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
cXY[0],
|
|
win.scroll["checklist"] + cXY[1],
|
|
width - cXY[0],
|
|
40,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
cXY[0]+50,
|
|
win.scroll["checklist"] + cXY[1]+25
|
|
)
|
|
layer.show_text(talk.text("add_new_task"))
|
|
|
|
|
|
|
|
tileX, current_Y = cXY
|
|
|
|
# So there would not be jumps of stuff. Let's add heigh to the scroll while
|
|
# in grab.
|
|
if win.current["tool"] == "grab":
|
|
current_Y = current_Y + height
|
|
|
|
|
|
|
|
# Outputting the layer
|
|
outlayer.set_source_surface(surface, x, y)
|
|
outlayer.paint()
|
|
|
|
# Scroll
|
|
UI_elements.scroll_area(outlayer, win, "checklist",
|
|
x+0,
|
|
y+50,
|
|
width,
|
|
height-50,
|
|
current_Y,
|
|
bar=True,
|
|
mmb=True)
|
|
|
|
|
|
|
|
|
|
|