2020-11-26 16:32:28 +01:00
|
|
|
# THIS FILE IS A PART OF VCStudio
|
|
|
|
# PYTHON 3
|
|
|
|
|
|
|
|
# This a console project manager.
|
|
|
|
|
|
|
|
import os
|
|
|
|
import math
|
2021-03-03 15:45:27 +01:00
|
|
|
import urllib3
|
2021-01-03 00:45:26 +01:00
|
|
|
import hashlib
|
2020-12-09 03:31:21 +01:00
|
|
|
import threading
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# GTK module ( Graphical interface
|
|
|
|
import gi
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
|
|
from gi.repository import Gtk
|
|
|
|
from gi.repository import Gdk
|
|
|
|
from gi.repository import GLib
|
|
|
|
from gi.repository import GdkPixbuf
|
|
|
|
import cairo
|
|
|
|
|
|
|
|
# Own modules
|
|
|
|
from settings import settings
|
|
|
|
from settings import talk
|
2020-12-07 22:41:24 +01:00
|
|
|
from settings import fileformats
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# UI
|
|
|
|
from UI import UI_color
|
|
|
|
|
|
|
|
def roundrect(layer, win, x, y, width, height, r, button=False, icon=False,
|
2021-01-11 21:50:20 +01:00
|
|
|
tip="", fill=True, url="", clip=False, offset=[0,0], text=False):
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# This function draws a rectangle with rounded edges.
|
|
|
|
|
|
|
|
# A button variable is a calable for the button action. Basically it's a
|
|
|
|
# function. Roundrect will act as a button.
|
|
|
|
if button:
|
|
|
|
|
|
|
|
#if not current url in the software
|
|
|
|
if url and url != win.url:
|
2020-12-01 18:35:15 +01:00
|
|
|
|
|
|
|
# So icons would not disappear LOL
|
|
|
|
if icon:
|
|
|
|
image(layer, win, "settings/themes/"\
|
|
|
|
+win.settings["Theme"]+"/icons/"+icon+".png", x, y)
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
# If UI testing is on preview. Buttons.
|
|
|
|
if win.current["testing"]:
|
|
|
|
UI_color.set(layer, win, "testing_banner")
|
|
|
|
layer.rectangle(x,y,width,height)
|
|
|
|
layer.stroke()
|
|
|
|
|
|
|
|
|
2020-12-04 14:30:42 +01:00
|
|
|
if win.current['mx'] in range(int(x+offset[0]), int(x+width+offset[0])) \
|
|
|
|
and win.current['my'] in range(int(y+offset[1]), int(y+height+offset[1])) :
|
2020-11-26 16:32:28 +01:00
|
|
|
do = True
|
|
|
|
|
|
|
|
if clip:
|
|
|
|
if win.current['mx'] in range(int(clip[0]), int(clip[0]+clip[2])) \
|
|
|
|
and win.current['my'] in range(int(clip[1]), int(clip[1]+clip[3])) :
|
|
|
|
do = True
|
|
|
|
else:
|
|
|
|
do = False
|
|
|
|
|
|
|
|
else:
|
|
|
|
do = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if do:
|
2021-01-11 21:50:20 +01:00
|
|
|
|
|
|
|
# cursor
|
|
|
|
if text:
|
|
|
|
win.current["cursor"] = win.cursors["text"]
|
|
|
|
else:
|
|
|
|
win.current["cursor"] = win.cursors["hand"]
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# If holding click
|
|
|
|
if win.current["LMB"]:
|
|
|
|
UI_color.set(layer, win, "button_clicked")
|
|
|
|
else:
|
|
|
|
UI_color.set(layer, win, "button_active")
|
|
|
|
# If clicked
|
|
|
|
if win.previous["LMB"] and not win.current["LMB"]:
|
2021-01-11 21:50:20 +01:00
|
|
|
win.current["cursor"] = win.cursors["watch"]
|
2020-11-26 16:32:28 +01:00
|
|
|
button()
|
|
|
|
|
|
|
|
# Button might have a tooltip as well
|
|
|
|
if tip:
|
|
|
|
tooltip(win, tip)
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
do = True
|
|
|
|
|
|
|
|
if do:
|
|
|
|
# Making sure that round rectangle will not be smaller then it's round-
|
|
|
|
# ness. Also with width and height zero, it's going to draw a circle.
|
|
|
|
|
|
|
|
if width < r*2:
|
|
|
|
width = r*2
|
|
|
|
if height < r*2:
|
|
|
|
height = r*2
|
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
# I just out of blue decided that I want to have a setting to restrict
|
|
|
|
# the amount of roundness for every roundrect. If the user want let him
|
|
|
|
# or her have ugly squares everywhere.
|
|
|
|
|
|
|
|
if "Roundness" not in win.settings:
|
|
|
|
win.settings["Roundness"] = 1.0
|
|
|
|
settings.write("Roundness", 1.0)
|
|
|
|
|
|
|
|
r = r*win.settings["Roundness"]
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# actuall drawing
|
|
|
|
layer.move_to(x,y+r)
|
|
|
|
layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
|
|
|
|
layer.arc(x+width-r, y+r, r, 3*math.pi/2, 0)
|
|
|
|
layer.arc(x+width-r, y+height-r, r, 0, math.pi/2)
|
|
|
|
layer.arc(x+r, y+height-r, r, math.pi/2, math.pi)
|
|
|
|
layer.close_path()
|
|
|
|
if fill:
|
|
|
|
layer.fill()
|
|
|
|
|
|
|
|
# Icon is a continuation of the button part. Because you need a way to see
|
|
|
|
# that that the button is even there to begin with.
|
|
|
|
if icon:
|
|
|
|
image(layer, win, "settings/themes/"\
|
|
|
|
+win.settings["Theme"]+"/icons/"+icon+".png", x, y)
|
|
|
|
|
|
|
|
def animate(name, win, v1=0, v2=None, time=10, force=False):
|
|
|
|
|
|
|
|
# This function will make animating values over time possible. For smooth
|
|
|
|
# Transisions and things like this it's going to be very usefull.
|
|
|
|
|
2020-12-09 03:31:21 +01:00
|
|
|
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# Let's clear mess in case they I'm lazy to make all the things
|
|
|
|
if v2 == None:
|
|
|
|
v2 = v1
|
|
|
|
|
|
|
|
# Set up the animation into the animations. If it's not there yet.
|
|
|
|
if name not in win.animations or force:
|
|
|
|
win.animations[name] = [
|
|
|
|
v1,
|
|
|
|
v2,
|
|
|
|
time,
|
|
|
|
win.current["frame"]
|
|
|
|
]
|
|
|
|
|
|
|
|
# Let's get data out of the win.animation[name]
|
|
|
|
v1 = win.animations[name][0]
|
|
|
|
v2 = win.animations[name][1]
|
|
|
|
time = win.animations[name][2]
|
|
|
|
start = win.animations[name][3]
|
|
|
|
frame = win.current["frame"]
|
|
|
|
|
2020-12-09 03:31:21 +01:00
|
|
|
if time == 0:
|
|
|
|
return v2
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# If animation is over.
|
|
|
|
if start + time < frame:
|
|
|
|
return v2
|
|
|
|
|
|
|
|
# If v1 and v2 are the same. I'm doing it here. In case the value would be
|
|
|
|
# Animated later. So it will create the animation instance.
|
|
|
|
if v1 == v2:
|
|
|
|
return v2
|
|
|
|
|
|
|
|
if v1 < v2:
|
|
|
|
vN = v1 + ((v2 - v1)/time*(frame-start))
|
|
|
|
else:
|
|
|
|
vN = v1 - ((v1 - v2)/time*(frame-start))
|
|
|
|
|
|
|
|
return vN
|
|
|
|
|
|
|
|
def blur(surface, win, amount):
|
|
|
|
|
|
|
|
# This function will blur a given layer by scaling it down and scaling it
|
|
|
|
# back up. It will be doing it only when a given blur setting it active.
|
|
|
|
|
|
|
|
# To avoid all kinds of Zero devision problems. And speed up the draw if
|
|
|
|
# using animated blur values.
|
|
|
|
if amount < 3: # When Blue value was less then 3 it felt sharp but not enough
|
|
|
|
return surface # Which caused sense of uneasiness.
|
|
|
|
|
|
|
|
# Setting up initial Blur
|
|
|
|
if not "Blur" in win.settings:
|
|
|
|
settings.write("Blur", True) # Writing to file
|
|
|
|
win.settings = settings.load_all() # Loading file back to RAM
|
|
|
|
|
|
|
|
# If to active blur. Will be changed in the graphics settings.
|
|
|
|
if win.settings["Blur"]:
|
|
|
|
# scaling down
|
|
|
|
surface1 = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
|
|
|
|
win.current['h'])
|
|
|
|
slacedownlayer = cairo.Context(surface1)
|
|
|
|
slacedownlayer.scale(1/amount,
|
|
|
|
1/amount)
|
|
|
|
|
|
|
|
slacedownlayer.set_source_surface(surface, 0 , 0)
|
|
|
|
slacedownlayer.paint()
|
|
|
|
|
|
|
|
#scaling back up
|
|
|
|
surface2 = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
|
|
|
|
win.current['h'])
|
|
|
|
slaceuplayer = cairo.Context(surface2)
|
|
|
|
slaceuplayer.scale(amount,
|
|
|
|
amount)
|
|
|
|
|
|
|
|
slaceuplayer.set_source_surface(surface1, 0 , 0)
|
|
|
|
slaceuplayer.paint()
|
|
|
|
|
|
|
|
return surface2
|
|
|
|
else:
|
|
|
|
return surface
|
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
def hash_file(f):
|
|
|
|
|
|
|
|
# This function will give me MD5 hashes of various files
|
|
|
|
|
|
|
|
try:
|
|
|
|
BLOCKSIZE = 65536
|
|
|
|
hasher = hashlib.md5()
|
|
|
|
with open(f, 'rb') as afile:
|
|
|
|
buf = afile.read(BLOCKSIZE)
|
|
|
|
while len(buf) > 0:
|
|
|
|
hasher.update(buf)
|
|
|
|
buf = afile.read(BLOCKSIZE)
|
|
|
|
return str(hasher.hexdigest())
|
|
|
|
except:
|
|
|
|
return "FOLDER"
|
|
|
|
|
|
|
|
def loadimage(layer, win ,path, x, y, width, height, fit, cell=0):
|
2021-03-03 15:45:27 +01:00
|
|
|
|
|
|
|
# Before we load an image we have to know whether the image is local
|
|
|
|
# in the Internet. Markdown files for example can have links to a
|
|
|
|
# picture in the internet. And so if a given Image URL is not local
|
|
|
|
# we have to be able to download it.
|
|
|
|
|
|
|
|
# For reasons we all can relate to. I don't want such downloading to
|
|
|
|
# happen automatically. Unless the user is going to enable auto
|
|
|
|
# download in the settings.
|
|
|
|
|
|
|
|
# Let's set up a setting. I'm doing it here because Images drawn
|
|
|
|
# first. On the main screen. And the setting does not exists in
|
|
|
|
# the earlier settings file from the earlier version.
|
|
|
|
|
|
|
|
if "auto_download_images" not in win.settings:
|
|
|
|
win.settings["auto_download_images"] = False
|
|
|
|
settings.write("auto_download_images", win.settings["auto_download_images"])
|
|
|
|
|
|
|
|
filename = "/tmp/"+path.replace("/","_")
|
|
|
|
tmppath = ""
|
|
|
|
|
|
|
|
if path.startswith("http") and not os.path.exists(filename):
|
|
|
|
|
|
|
|
|
|
|
|
if win.images[cell][path]["image"] != "downloading":
|
|
|
|
|
|
|
|
# If the button is not pressed yet. Then we need to
|
|
|
|
# put a downlod button. Look down where implement
|
|
|
|
# the drawing of the image for the button it self.
|
|
|
|
|
|
|
|
win.images[cell][path]["loading"] = False
|
|
|
|
win.images[cell][path]["image"] = "download_button"
|
|
|
|
return
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# Now somebody either pressed the button. Or it set to
|
|
|
|
# automatic downloads. Then, let's get the file.
|
|
|
|
|
|
|
|
win.images[cell][path]["image"] = None
|
|
|
|
|
|
|
|
http = urllib3.PoolManager()
|
|
|
|
r = http.request('GET', path, preload_content=False)
|
|
|
|
with open(filename, 'wb') as out:
|
|
|
|
while True:
|
|
|
|
data = r.read(1024)
|
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
out.write(data)
|
|
|
|
|
|
|
|
r.release_conn()
|
|
|
|
|
|
|
|
|
|
|
|
elif path.startswith("http"):
|
|
|
|
tmppath = path
|
|
|
|
path = filename
|
|
|
|
|
|
|
|
|
|
|
|
# Also I don't want downloading to happen here. Since if it's not
|
|
|
|
# downloaded
|
2021-01-03 00:45:26 +01:00
|
|
|
|
|
|
|
# So multiple types of the image could be loaded. It could be either
|
|
|
|
# an image file. Like png or jpeg. Either a video file. Or a blend file
|
|
|
|
# each with it's own loading problems.
|
2021-03-03 15:45:27 +01:00
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
# While cairo can read pngs directly. It's not the best way of doing it
|
|
|
|
# since it's not capable of reading jpegs and other files. So let's do
|
|
|
|
# something about it.
|
|
|
|
|
|
|
|
foundformat = False
|
|
|
|
|
|
|
|
# IMAGEFILES
|
|
|
|
for f in fileformats.images:
|
|
|
|
if path.endswith(f):
|
|
|
|
|
|
|
|
foundformat = True
|
|
|
|
|
|
|
|
# So this parth of the code is for loading all simple images. From
|
|
|
|
# pngs Jpegs to anything else.
|
|
|
|
|
|
|
|
# We are going to use Gtk's GdkPixbuf for this. We will need to
|
|
|
|
# convert it to cairo surface later
|
|
|
|
|
|
|
|
try:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file(path)
|
|
|
|
except:
|
2021-03-03 15:45:27 +01:00
|
|
|
|
2021-01-03 14:47:00 +01:00
|
|
|
try:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/image.png")
|
|
|
|
except:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/Default/icons/image.png")
|
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
# VIDEOFILES
|
|
|
|
for f in fileformats.videos:
|
|
|
|
if path.endswith(f):
|
|
|
|
|
|
|
|
foundformat = True
|
|
|
|
|
|
|
|
# Now if it's a video. It's going to be a little harder. We will need
|
|
|
|
# to use totem. It's a video player that exists in many GNU/Linux
|
|
|
|
# systems. Unfortunatly not everybody could see video previews.
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
# This is going to require a few steps.
|
|
|
|
|
|
|
|
# 1. Making a filename for our totem to output.
|
|
|
|
part = path.replace("/", "_").replace(" ", "_")
|
|
|
|
|
|
|
|
# 2. Calling totem. And telling him that we want a thumbnail.
|
|
|
|
os.system("totem-video-thumbnailer -s "+str(width)+" "+path\
|
|
|
|
+" /tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
|
|
|
|
# 3. Loading this thumbnail.
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
|
|
|
|
# 4. Cleaning the thumbnail from the OS.
|
|
|
|
try:
|
|
|
|
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
except:
|
2021-01-03 14:47:00 +01:00
|
|
|
try:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/video.png")
|
|
|
|
except:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/Default/icons/video.png")
|
2021-01-03 00:45:26 +01:00
|
|
|
# BLEND FILES
|
|
|
|
|
|
|
|
for f in ["blend", "blend1"]:
|
|
|
|
if path.endswith(f):
|
|
|
|
|
|
|
|
foundformat = True
|
|
|
|
|
|
|
|
# Similarly to the Video. Blends files has their own thumbnailer. This
|
|
|
|
# time it's inside of the code of VCStudio. But I've copied it from
|
|
|
|
# the blender's installation folder. So you can find it there too
|
|
|
|
# it's called blender-thumbnailer.py. Which is a python script. Which
|
|
|
|
# is cool.
|
|
|
|
|
|
|
|
# Because thumbnailer is developed to work regardless of whether blender
|
|
|
|
# is installed or not. As it's reading the blend file directly. And
|
|
|
|
# not using some bpy script. We can copy that over to VCStudio and use
|
|
|
|
# it to give previews to users who do not have blender installed.
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
# This is going to require a few steps.
|
|
|
|
|
|
|
|
# 1. Making a filename for our thumbnailer to output.
|
|
|
|
part = path.replace("/", "_").replace(" ", "_")
|
|
|
|
|
|
|
|
# 2. Calling thumbnailer. And telling him that we want a thumbnail.
|
|
|
|
os.system("python3 "+os.getcwd()+"/UI/blender-thumbnailer.py "\
|
|
|
|
+path+" /tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
|
|
|
|
# 3. Loading this thumbnail.
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
|
|
|
|
# 4. Cleaning the thumbnail from the OS.
|
|
|
|
try:
|
|
|
|
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
2021-01-03 14:47:00 +01:00
|
|
|
try:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/blender.png")
|
|
|
|
except:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/Default/icons/blender.png")
|
2021-01-03 00:45:26 +01:00
|
|
|
|
|
|
|
if not foundformat:
|
|
|
|
|
|
|
|
# If you can't find any format. Just use the file icon then
|
2021-01-03 14:47:00 +01:00
|
|
|
try:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/file.png")
|
|
|
|
except:
|
|
|
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/Default/icons/file.png")
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
# Then to convert the pixbuf to a cairo surface
|
|
|
|
Px = load1.get_width()
|
|
|
|
Py = load1.get_height()
|
|
|
|
load2 = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
|
|
|
|
imagedraw = cairo.Context(load2)
|
|
|
|
Gdk.cairo_set_source_pixbuf( imagedraw, load1, 0, 0)
|
|
|
|
imagedraw.paint()
|
|
|
|
|
|
|
|
# If I want to resize the image for an icon or something. There is
|
|
|
|
# gonna be the folowing algorythm.
|
|
|
|
|
|
|
|
if width or height:
|
|
|
|
|
|
|
|
dx = 0
|
|
|
|
dy = 0
|
|
|
|
|
|
|
|
|
|
|
|
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
|
|
|
imagedraw = cairo.Context(imagesurface)
|
|
|
|
|
|
|
|
# Crop effect. Really hard on my brains.
|
|
|
|
|
|
|
|
if fit == 'crop':
|
|
|
|
if height > load2.get_height()\
|
|
|
|
or width > load2.get_width():
|
|
|
|
|
|
|
|
dx = (width/2) -(load2.get_width() /2)
|
|
|
|
dy = (height/2)-(load2.get_height()/2)
|
|
|
|
|
|
|
|
else:
|
|
|
|
factor = 1
|
|
|
|
if (load2.get_height()*(width/load2.get_width()))\
|
|
|
|
< height:
|
|
|
|
factor = height / load2.get_height()
|
|
|
|
else:
|
|
|
|
factor = width / load2.get_width()
|
|
|
|
#factor = 0.1
|
|
|
|
imagedraw.scale(factor, factor)
|
|
|
|
dx = (width/2)/factor -(load2.get_width() /2)
|
|
|
|
dy = (height/2)/factor -(load2.get_height()/2)
|
2021-03-03 15:45:27 +01:00
|
|
|
|
|
|
|
# Finally I need to implement something but the Crop.
|
|
|
|
# Fit Width I want to use in MarkDowns. Maybe also in the
|
|
|
|
# text of the script. Makes sense.
|
|
|
|
|
|
|
|
elif fit == "fit_width" and load2.get_width() > width:
|
|
|
|
factor = width / load2.get_width()
|
|
|
|
dx = 0
|
|
|
|
dy = 0
|
|
|
|
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(load2.get_height()*factor))
|
|
|
|
imagedraw = cairo.Context(imagesurface)
|
|
|
|
imagedraw.scale(factor, factor)
|
|
|
|
elif fit == "fit_width":
|
|
|
|
dx = 0
|
|
|
|
dy = 0
|
|
|
|
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(load2.get_width()), int(load2.get_height()))
|
|
|
|
imagedraw = cairo.Context(imagesurface)
|
|
|
|
|
|
|
|
|
2021-01-03 14:47:00 +01:00
|
|
|
# Let's make an ability for theme makers to simply color the standard
|
|
|
|
# icons into any shade.
|
2021-01-03 00:45:26 +01:00
|
|
|
|
2021-01-03 14:47:00 +01:00
|
|
|
imagedraw.set_source_surface(load2, dx, dy)
|
2021-01-03 00:45:26 +01:00
|
|
|
imagedraw.paint()
|
|
|
|
else:
|
|
|
|
imagesurface = load2
|
|
|
|
|
|
|
|
# Saving it into the win.images
|
2021-03-03 15:45:27 +01:00
|
|
|
if tmppath:
|
|
|
|
path = tmppath
|
2021-01-03 00:45:26 +01:00
|
|
|
win.images[cell][path]["loading"] = False
|
|
|
|
win.images[cell][path]["image"] = imagesurface
|
|
|
|
win.images[cell][path]["hash"] = hash_file(path)
|
|
|
|
|
|
|
|
win.imageload -= 1
|
|
|
|
|
|
|
|
def reload_images(win):
|
|
|
|
|
|
|
|
# This function will force the images to refrash. For things like sync
|
|
|
|
# or simply where a file is changed. Or for any other reason. Now we
|
|
|
|
# probably don't need to do this on every image. So instead we are going
|
|
|
|
# to compare the hashes of the current images to the ones of the files
|
|
|
|
# and if they changed. We are going to update those.
|
|
|
|
|
|
|
|
for cell in list(win.images.keys()):
|
|
|
|
for image in list(win.images[cell].keys()):
|
|
|
|
if hash_file(image) != win.images[cell][image]["hash"]:
|
|
|
|
win.images[cell][image]["loading"] = True
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
2021-03-03 15:45:27 +01:00
|
|
|
def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0, offset=[0,0]):
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# This module will handle drawing images to the layers. It's not that hard
|
|
|
|
# to do in cairo by default. But i'm doing it at every frame. And so it
|
|
|
|
# means a system of images should exist to take out the load. Basically
|
|
|
|
# it will make sure the images is loaded only ones. And for the next draw
|
|
|
|
# calls it will forward the old image.
|
|
|
|
|
|
|
|
# Attempt of optimization. Keeping in mind the nature of the programm. Basi-
|
|
|
|
# cally I will not load image unless x and y are in frame. Kind a ugly. I
|
|
|
|
# know. But I don't want to even bother checking the resolution of the image
|
|
|
|
# if it's not in a frame. Roughly speaking. Will see maybe I will make do
|
|
|
|
# something to make it better.
|
|
|
|
|
2020-12-10 23:14:48 +01:00
|
|
|
if cell not in win.images:
|
|
|
|
win.images[cell] = {}
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
if int(x) not in range(int(0-width ), int(win.current["w"])) or \
|
2021-03-03 15:45:27 +01:00
|
|
|
int(y) not in range(int(0-height-win.current["h"]), int(win.current["h"])) :
|
2020-11-26 16:32:28 +01:00
|
|
|
return
|
|
|
|
|
|
|
|
# If you ran this software you probably noticed that images are loading
|
|
|
|
# dynamically. I did it using this following crazy algorythm borowed from
|
2021-01-03 00:45:26 +01:00
|
|
|
# the old organizer.
|
|
|
|
|
|
|
|
# Here I want to load images. But I also want to give myself an ability to
|
|
|
|
# load refrash images. Without refrashing all the images. For this I'm going
|
|
|
|
# to use hashes. Specificly MD5 but it doesn't matter really. The idea is
|
|
|
|
# that on a special redraw call. We are going to check hashes storred with
|
|
|
|
# those from the file. And only if a file really changed. Then update the image.
|
2020-11-26 16:32:28 +01:00
|
|
|
|
2021-01-03 14:47:00 +01:00
|
|
|
|
|
|
|
# First the icon might not exist in the icon pack. Since I want to make
|
|
|
|
# packs using
|
|
|
|
if win.settings["Theme"] in path and not os.path.exists(path):
|
|
|
|
path = path.replace(win.settings["Theme"], "Default")
|
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
if path not in win.images[cell] or win.images[cell][path]["loading"]:
|
2020-11-26 16:32:28 +01:00
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
# If this is the first time this image is draw we want to create it a data
|
|
|
|
# structure. So we could load the image.
|
2020-11-26 16:32:28 +01:00
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
win.images[cell][path] = {
|
|
|
|
"loading": True, # Whether the image is currently loading.
|
|
|
|
"image" : None, # The image data it self.
|
|
|
|
"hash" : "" # The hash of the image file.
|
|
|
|
}
|
|
|
|
|
|
|
|
# Then we want to insure to load only a given amount of images in the same
|
|
|
|
# time. For this we are going to use the next variable.
|
|
|
|
|
|
|
|
MAXLOAD = 10
|
|
|
|
|
|
|
|
if win.imageload < MAXLOAD:
|
|
|
|
win.imageload += 1
|
|
|
|
|
|
|
|
|
|
|
|
# Now we want to load the image. But we are going to do that in a separate thread
|
|
|
|
# not to freeze the UI while the image is loading.
|
|
|
|
|
|
|
|
# See: loadimage() function for the continuation of this.
|
2020-11-26 16:32:28 +01:00
|
|
|
|
2021-01-03 00:45:26 +01:00
|
|
|
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit, cell))
|
2020-12-09 03:31:21 +01:00
|
|
|
t.start()
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
#loading it back
|
|
|
|
else:
|
2021-03-03 15:45:27 +01:00
|
|
|
if win.images[cell][path]["image"] and win.images[cell][path]["image"] != "download_button":
|
2021-01-03 00:45:26 +01:00
|
|
|
imagesurface = win.images[cell][path]["image"]
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# Writting the image to the screen
|
2021-01-03 14:47:00 +01:00
|
|
|
|
|
|
|
|
2021-03-03 15:45:27 +01:00
|
|
|
try:
|
|
|
|
if "icons" in win.color and "settings/themes/" in path:
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "icons")
|
|
|
|
layer.rectangle(x,y,0,0)
|
|
|
|
layer.fill()
|
|
|
|
|
|
|
|
layer.mask_surface(imagesurface, x, y)
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "icons")
|
|
|
|
layer.fill()
|
|
|
|
else:
|
|
|
|
layer.set_source_surface(imagesurface, x, y)
|
|
|
|
layer.paint()
|
|
|
|
except:
|
|
|
|
pass
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# And if testing
|
|
|
|
|
|
|
|
if win.current["testing"]:
|
|
|
|
UI_color.set(layer, win, "testing_image")
|
|
|
|
layer.rectangle(x,y,imagesurface.get_width(),imagesurface.get_height())
|
|
|
|
layer.stroke()
|
2021-03-03 15:45:27 +01:00
|
|
|
|
|
|
|
elif win.images[cell][path]["image"] == "download_button":
|
|
|
|
|
|
|
|
# If the image is online. By default it will not load it unless the
|
|
|
|
# user presses a download button. So here it is.
|
|
|
|
|
|
|
|
def do():
|
|
|
|
win.images[cell][path]["image"] = "downloading"
|
|
|
|
loadimage(layer, win ,path, x, y, width, height, fit, cell)
|
|
|
|
win.images[cell][path]["loading"] = True
|
|
|
|
|
|
|
|
# Some link image will be automatically clicked. This
|
|
|
|
# fixes the problem.
|
|
|
|
win.current["LMB"] = False
|
|
|
|
win.previous["LMB"] = False
|
|
|
|
|
|
|
|
|
|
|
|
if win.settings["auto_download_images"]:
|
|
|
|
t = threading.Thread(target=do)
|
|
|
|
t.start()
|
|
|
|
else:
|
|
|
|
|
|
|
|
roundrect(layer, win, x,y,40,40,10,
|
|
|
|
icon="image_link",
|
|
|
|
button=do,
|
|
|
|
offset=offset,
|
|
|
|
tip=path)
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
def tooltip(win, text):
|
|
|
|
|
|
|
|
layer = win.tooltip
|
|
|
|
|
|
|
|
# This function draws a tooltip helper window.
|
|
|
|
|
|
|
|
# Just in case
|
|
|
|
text = str(text)
|
|
|
|
|
|
|
|
# Let's get dimantions of the cube first.
|
|
|
|
lines = 0
|
|
|
|
maxletters = 0
|
|
|
|
|
|
|
|
for line in text.split("\n"):
|
|
|
|
lines += 1
|
|
|
|
|
|
|
|
if len(line) > maxletters:
|
|
|
|
maxletters = len(line)
|
|
|
|
|
|
|
|
|
|
|
|
# Now when we now the mount of lines and the max lenght of a line. We can
|
|
|
|
# start actually drawing something.
|
|
|
|
|
|
|
|
# Let's try to make so it's not out of the frame.
|
|
|
|
sx = win.current["mx"]
|
|
|
|
sy = win.current["my"]
|
|
|
|
|
|
|
|
if sx+(maxletters*9)+40 > win.current["w"]:
|
|
|
|
sx = win.current["w"] - ((maxletters*9)+40)
|
|
|
|
if sy+(lines*20)+10 > win.current["h"]:
|
|
|
|
sy = win.current["h"] - ((lines*20)+10)
|
|
|
|
|
|
|
|
# Rectangle
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
|
|
roundrect(layer, win,
|
|
|
|
sx,
|
|
|
|
sy,
|
|
|
|
(maxletters*9)+40,
|
|
|
|
(lines*20)+10,
|
|
|
|
10)
|
|
|
|
|
|
|
|
|
|
|
|
# Text it self
|
|
|
|
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL,
|
|
|
|
cairo.FONT_WEIGHT_NORMAL)
|
|
|
|
layer.set_font_size(15)
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
|
|
|
|
|
|
for num, line in enumerate(text.split("\n")):
|
|
|
|
layer.move_to(sx+20,
|
|
|
|
sy+20+(20*num) )
|
|
|
|
layer.show_text(line)
|
|
|
|
|
|
|
|
def scroll_area(layer, win, name, x, y, width, height, maxlength,
|
|
|
|
bar=False,sideways=False, mmb=False, mmb_only=False,
|
2020-12-15 21:42:56 +01:00
|
|
|
url="", strenght=50, bar_always=False):
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# This function going to handle all the scrolling windows. Making it so it's
|
|
|
|
# relativelly simple to set up big widgets with in small areas.
|
|
|
|
# It will handle only the value of the scroll stored in win.scroll[name]
|
|
|
|
|
|
|
|
if maxlength == 0:
|
|
|
|
maxlength = 1
|
|
|
|
|
2020-12-10 23:14:48 +01:00
|
|
|
x = int(x)
|
|
|
|
y = int(y)
|
|
|
|
width = int(width)
|
|
|
|
height = int(height)
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# First let's set one up if it's not setup
|
|
|
|
if name not in win.scroll:
|
|
|
|
win.scroll[name] = 0
|
|
|
|
return
|
|
|
|
|
|
|
|
# Getting scroll amount based on all kinds of settings. AT THIS MOMENT
|
|
|
|
# IT'S IN AN ALPHA BECAUSE I'M LAZY. I GONNA IMPLEMENT THING AS I NEED THEM
|
|
|
|
# Or you can do that. IDK...
|
|
|
|
|
|
|
|
amount = 0.0
|
|
|
|
if win.current['mx'] in range(x, x+width) \
|
|
|
|
and win.current['my'] in range(y, y+height) :
|
|
|
|
if not mmb_only:
|
2020-12-13 04:07:37 +01:00
|
|
|
|
|
|
|
amount = win.current["scroll"][1]*strenght
|
|
|
|
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
if mmb_only:
|
|
|
|
mmb = True
|
|
|
|
|
|
|
|
# Middle mouse button scroll, or using a graphical tablet.
|
|
|
|
if mmb:
|
|
|
|
if win.current["MMB"]:
|
2020-12-13 04:07:37 +01:00
|
|
|
if not sideways:
|
|
|
|
amount = 0 - ( win.current["my"] - win.previous["my"] )
|
|
|
|
else:
|
|
|
|
amount = 0 - ( win.current["mx"] - win.previous["mx"] )
|
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
# I guess I need to separate the logic into a separate minifunction.
|
|
|
|
# I will use later for the scroll bar thingy. So not to rewrite the code
|
|
|
|
# Here is a function thingy.
|
|
|
|
|
|
|
|
def logic():
|
|
|
|
if url and url != win.url:
|
|
|
|
return
|
|
|
|
# Scroll logic
|
|
|
|
win.scroll[name] -= amount
|
|
|
|
|
2020-12-13 04:07:37 +01:00
|
|
|
if not sideways:
|
|
|
|
# If too low
|
|
|
|
if win.scroll[name] < (1-maxlength+height):
|
|
|
|
win.scroll[name] = (1-maxlength+height)
|
|
|
|
# If too high
|
|
|
|
if win.scroll[name] > 0:
|
|
|
|
win.scroll[name] = 0
|
|
|
|
else:
|
|
|
|
# If too low
|
|
|
|
if win.scroll[name] < (1-maxlength+width):
|
|
|
|
win.scroll[name] = (1-maxlength+width)
|
|
|
|
# If too high
|
|
|
|
if win.scroll[name] > 0:
|
|
|
|
win.scroll[name] = 0
|
2020-11-26 16:32:28 +01:00
|
|
|
logic()
|
|
|
|
# Not BAR. Which is going to be drawn at a side of what ever content there
|
|
|
|
# Basically a scrollbar. But implemented from scratch. Because I'm crazy.
|
|
|
|
# and have nothing better to do now.
|
|
|
|
|
|
|
|
if bar:
|
|
|
|
|
|
|
|
# For now I'm going to implement only vertical bar. I gonna implement
|
|
|
|
# the horisontal one when the time comes.
|
|
|
|
|
|
|
|
if not sideways:
|
|
|
|
|
|
|
|
# Fist let's make some math in front. Because things like this
|
|
|
|
# require ton of maths. And it's good to have some predone.
|
|
|
|
|
|
|
|
tobreak = False # Also if we can abort the operation early with it.
|
|
|
|
fraction = height / maxlength # Similar to progress bar for now
|
|
|
|
if fraction > 1:
|
|
|
|
tobreak = True
|
2020-12-15 21:42:56 +01:00
|
|
|
fraction = 1
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# To break parameter basically says. To draw it the bar only when
|
|
|
|
# it's actully needed. When content aka maxlength is bigger then
|
|
|
|
# our viewport.
|
|
|
|
|
2020-12-15 21:42:56 +01:00
|
|
|
if not tobreak or bar_always:
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# Now the offset value. That will move our progress bar with
|
|
|
|
# the scroll value.
|
|
|
|
|
|
|
|
offset = (height-60)*( (1-win.scroll[name]) / maxlength )
|
|
|
|
|
|
|
|
# Background bar
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "background")
|
|
|
|
roundrect(layer, win,
|
|
|
|
(x+width)-20,
|
|
|
|
y+30,
|
|
|
|
10,
|
|
|
|
height-60,
|
|
|
|
5
|
|
|
|
)
|
|
|
|
|
|
|
|
# Active bar
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "button_active")
|
|
|
|
|
|
|
|
# Let's save a little bit more math because it's going to be
|
|
|
|
# vild. And I love it.
|
|
|
|
|
|
|
|
Lx = (x+width)-20
|
|
|
|
LSx = 10
|
|
|
|
Ly = y+30+offset
|
|
|
|
LSy = (height-60)*fraction
|
|
|
|
|
|
|
|
# Mouse over thingy. To make the bat a tiny bit brighter.
|
|
|
|
# indicating to the user that it's now could be used.
|
|
|
|
|
|
|
|
if win.current['mx'] in range(int(Lx), int(Lx+LSx)) \
|
|
|
|
and win.current['my'] in range(int(Ly), int(Lx+LSy)) :
|
|
|
|
UI_color.set(layer, win, "button_clicked")
|
|
|
|
|
|
|
|
# Now separatelly we gonna check for if the mouse pressed.
|
|
|
|
# Remember if it's not pressed it's False. It's written in one
|
|
|
|
# of the files. Basically I want to be able to move the mouse
|
|
|
|
# outside the bar while moving. And so it won't cancel the motion.
|
|
|
|
|
|
|
|
# It seems like I did something wrong. Anyways it works.
|
|
|
|
|
|
|
|
if win.current["LMB"]:
|
|
|
|
if int(win.current['LMB'][0]) in range(int(Lx), int(Lx+LSx)) \
|
2020-12-09 03:31:21 +01:00
|
|
|
and int(win.current['LMB'][1]) in range(int(y), int(y+(height-60))) :
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
UI_color.set(layer, win, "button_clicked")
|
|
|
|
|
|
|
|
# A bit more math to set the value back.
|
|
|
|
amount = ( win.current["my"] - win.previous["my"] ) / \
|
|
|
|
(height-60) * maxlength
|
|
|
|
logic() # Yeah. Look a few lines back.
|
|
|
|
|
|
|
|
# And only after all of this nonsense we can draw the cube. Or
|
|
|
|
# should I say roundrect? A button? Aaaaaa....
|
|
|
|
|
|
|
|
roundrect(layer, win,
|
|
|
|
Lx,
|
|
|
|
Ly,
|
|
|
|
LSx,
|
|
|
|
LSy,
|
|
|
|
5
|
|
|
|
)
|
2020-12-13 04:07:37 +01:00
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# Fist let's make some math in front. Because things like this
|
|
|
|
# require ton of maths. And it's good to have some predone.
|
|
|
|
|
|
|
|
tobreak = False # Also if we can abort the operation early with it.
|
|
|
|
fraction = width / maxlength # Similar to progress bar for now
|
|
|
|
if fraction > 1:
|
|
|
|
tobreak = True
|
2020-12-15 21:42:56 +01:00
|
|
|
fraction = 1
|
2020-12-13 04:07:37 +01:00
|
|
|
|
|
|
|
# To break parameter basically says. To draw it the bar only when
|
|
|
|
# it's actully needed. When content aka maxlength is bigger then
|
|
|
|
# our viewport.
|
|
|
|
|
2020-12-15 21:42:56 +01:00
|
|
|
if not tobreak or bar_always:
|
2020-12-13 04:07:37 +01:00
|
|
|
|
|
|
|
# Now the offset value. That will move our progress bar with
|
|
|
|
# the scroll value.
|
|
|
|
|
|
|
|
offset = (width-60)*( (1-win.scroll[name]) / maxlength )
|
|
|
|
|
|
|
|
# Background bar
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "background")
|
|
|
|
roundrect(layer, win,
|
|
|
|
x+30,
|
|
|
|
(y+height)-20,
|
|
|
|
width-60,
|
|
|
|
10,
|
|
|
|
5
|
|
|
|
)
|
2020-11-26 16:32:28 +01:00
|
|
|
|
2020-12-13 04:07:37 +01:00
|
|
|
# Active bar
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "button_active")
|
|
|
|
|
|
|
|
# Let's save a little bit more math because it's going to be
|
|
|
|
# vild. And I love it.
|
|
|
|
|
|
|
|
Lx = (y+height)-20
|
|
|
|
LSx = 10
|
|
|
|
Ly = x+30+offset
|
|
|
|
LSy = (width-60)*fraction
|
|
|
|
|
|
|
|
# Mouse over thingy. To make the bat a tiny bit brighter.
|
|
|
|
# indicating to the user that it's now could be used.
|
|
|
|
|
|
|
|
if win.current['my'] in range(int(Lx), int(Lx+LSx)) \
|
|
|
|
and win.current['mx'] in range(int(Ly), int(Lx+LSy)) :
|
|
|
|
UI_color.set(layer, win, "button_clicked")
|
|
|
|
|
|
|
|
# Now separatelly we gonna check for if the mouse pressed.
|
|
|
|
# Remember if it's not pressed it's False. It's written in one
|
|
|
|
# of the files. Basically I want to be able to move the mouse
|
|
|
|
# outside the bar while moving. And so it won't cancel the motion.
|
|
|
|
|
|
|
|
# It seems like I did something wrong. Anyways it works.
|
|
|
|
|
|
|
|
if win.current["LMB"]:
|
|
|
|
if int(win.current['LMB'][1]) in range(int(Lx), int(Lx+LSx)) \
|
|
|
|
and int(win.current['LMB'][0]) in range(int(x), int(x+(width-60))) :
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "button_clicked")
|
|
|
|
|
|
|
|
# A bit more math to set the value back.
|
|
|
|
amount = ( win.current["mx"] - win.previous["mx"] ) / \
|
|
|
|
(width-60) * maxlength
|
|
|
|
logic() # Yeah. Look a few lines back.
|
|
|
|
|
|
|
|
# And only after all of this nonsense we can draw the cube. Or
|
|
|
|
# should I say roundrect? A button? Aaaaaa....
|
|
|
|
|
|
|
|
roundrect(layer, win,
|
|
|
|
Ly,
|
|
|
|
Lx,
|
|
|
|
LSy,
|
|
|
|
LSx,
|
|
|
|
5
|
|
|
|
)
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
def text(outlayer, win, name, x, y, width, height, set_text="", parse=False, fill=True,
|
2020-12-11 18:21:19 +01:00
|
|
|
editable=True, multiline=False , linebreak=False, centered=False, tip="",
|
|
|
|
offset=[0,0]):
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# This function will handle all the text writting in the software.
|
|
|
|
# I'm not sure about how parsing going to work for script files later.
|
|
|
|
# But if it's currently works, means that I already implemented it into
|
|
|
|
# the program.
|
|
|
|
|
|
|
|
# Making the layer
|
2020-12-07 22:41:24 +01:00
|
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
|
2020-11-26 16:32:28 +01:00
|
|
|
layer = cairo.Context(surface)
|
|
|
|
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
|
|
|
layer.set_font_size(20)
|
|
|
|
|
|
|
|
# Some challenges that it will have is how to correctly store data about
|
|
|
|
# the text in the system. I think we can use the win.text variable to store
|
|
|
|
# directories of the data.
|
|
|
|
|
|
|
|
if name not in win.text:
|
|
|
|
|
|
|
|
# I need to get something done before I can pu scroll in.
|
|
|
|
scrollname = name
|
|
|
|
while scrollname in win.scroll:
|
|
|
|
scrollname = scrollname+"_text"
|
|
|
|
|
|
|
|
win.text[name] = {
|
|
|
|
"text" :set_text, # Actuall text you are editing.
|
|
|
|
"cursor":[len(str(set_text)),len(str(set_text))], # Cursor
|
|
|
|
"insert":False, # Whether the insert mode is on
|
|
|
|
"scroll":scrollname # If multiline. The pointer for the scroll value.
|
|
|
|
}
|
|
|
|
|
|
|
|
# Background
|
|
|
|
if fill:
|
|
|
|
UI_color.set(layer, win, "darker_parts")
|
|
|
|
roundrect(layer, win,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
10)
|
|
|
|
layer.fill()
|
|
|
|
|
|
|
|
# Now after filling it up. I want to clip everything. SO no text will get
|
|
|
|
# out of a given area.
|
|
|
|
roundrect(layer, win,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
10,
|
|
|
|
fill=False)
|
|
|
|
layer.clip()
|
|
|
|
|
|
|
|
# Now I want to give a preview of the text it self. BUT. I need to be sure
|
|
|
|
# that if the text longer then a given width and there is no multiline or a
|
|
|
|
# linebreak. Then it scrolls sideways to the cursor.
|
|
|
|
|
|
|
|
# Automatic scroll system: Based on the position of the cursor.
|
|
|
|
offsetX = 0
|
|
|
|
cursor2location = win.text[name]["cursor"][1]*12 + offsetX
|
2020-12-15 21:42:56 +01:00
|
|
|
while cursor2location > width - 5:
|
2020-11-26 16:32:28 +01:00
|
|
|
offsetX -= 1
|
|
|
|
cursor2location = win.text[name]["cursor"][1]*12 + offsetX
|
|
|
|
|
|
|
|
|
|
|
|
# Text selection. AKA cursor
|
|
|
|
# So here we draw the cursor
|
|
|
|
if editable:
|
|
|
|
UI_color.set(layer, win, "node_blendfile")
|
2020-12-17 07:33:55 +01:00
|
|
|
if win.text[name]["cursor"][0] == win.text[name]["cursor"][1]:
|
|
|
|
if win.blink:
|
|
|
|
layer.rectangle(
|
|
|
|
win.text[name]["cursor"][0]*12+5 +offsetX,
|
|
|
|
5,
|
|
|
|
(win.text[name]["cursor"][1]*12)-(win.text[name]["cursor"][0]*12)+2,
|
|
|
|
30
|
|
|
|
)
|
2020-11-26 16:32:28 +01:00
|
|
|
else:
|
|
|
|
roundrect(layer, win,
|
|
|
|
win.text[name]["cursor"][0]*12+5 +offsetX,
|
|
|
|
5,
|
|
|
|
(win.text[name]["cursor"][1]*12)-(win.text[name]["cursor"][0]*12)+2,
|
|
|
|
30,
|
|
|
|
5,
|
|
|
|
fill=False
|
|
|
|
)
|
|
|
|
if win.textactive == name:
|
|
|
|
layer.fill()
|
2020-12-09 20:17:30 +01:00
|
|
|
elif win.text[name]["cursor"][0] != win.text[name]["cursor"][1]:
|
2020-11-26 16:32:28 +01:00
|
|
|
layer.stroke()
|
|
|
|
|
|
|
|
|
|
|
|
# Making sure that cursor is correct. Because a lot of bugs are happening
|
|
|
|
# with it and it's not cool.
|
|
|
|
|
|
|
|
# Mouse select
|
|
|
|
if win.current["LMB"]:
|
2020-12-11 18:21:19 +01:00
|
|
|
if int(win.current["mx"]-offset[0]) in range(int(x), int(x+width))\
|
|
|
|
and int(win.current["my"]-offset[1]) in range(int(y), int(y+height)):
|
|
|
|
win.text[name]["cursor"][0] = int((win.current["LMB"][0]-x-offsetX-offset[0])/12)
|
|
|
|
win.text[name]["cursor"][1] = int((win.current["mx"]-x-offsetX-offset[0])/12)
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
# If second part of selection ends up bigger then the first. Reverse them.
|
|
|
|
if win.text[name]["cursor"][0] > win.text[name]["cursor"][1]:
|
|
|
|
win.text[name]["cursor"] = [
|
|
|
|
win.text[name]["cursor"][1],
|
|
|
|
win.text[name]["cursor"][0]]
|
|
|
|
|
|
|
|
# If any part ends up beyond the text. Clip them in.
|
|
|
|
if win.text[name]["cursor"][0] < 0:
|
|
|
|
win.text[name]["cursor"][0] = 0
|
|
|
|
if win.text[name]["cursor"][1] < 0:
|
|
|
|
win.text[name]["cursor"][1] = 0
|
|
|
|
if win.text[name]["cursor"][0] > len(str(win.text[name]["text"])):
|
|
|
|
win.text[name]["cursor"][0] = len(str(win.text[name]["text"]))
|
|
|
|
if win.text[name]["cursor"][1] > len(str(win.text[name]["text"])):
|
|
|
|
win.text[name]["cursor"][1] = len(str(win.text[name]["text"]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Drawing the text
|
|
|
|
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
|
|
layer.move_to(5+offsetX, height/2+5)
|
|
|
|
if centered:
|
|
|
|
layer.move_to(width/2-len(str(win.text[name]["text"]))*12/2, height/2+5)
|
|
|
|
layer.show_text(str(win.text[name]["text"]))
|
|
|
|
|
|
|
|
# Editing the text
|
|
|
|
if win.current["keys"] and editable and name == win.textactive:
|
|
|
|
# Let's filter the input first.
|
|
|
|
# For example
|
|
|
|
if not multiline: #Removing enter key press
|
2020-12-11 18:21:19 +01:00
|
|
|
if 65293 in win.current["keys"] or 65421 in win.current["keys"]\
|
|
|
|
or 65289 in win.current["keys"]: # TAB
|
2020-11-26 16:32:28 +01:00
|
|
|
win.current["key_letter"] = ""
|
2020-12-11 18:21:19 +01:00
|
|
|
|
2020-11-26 16:32:28 +01:00
|
|
|
|
|
|
|
prevlen = len(win.text[name]["text"])
|
|
|
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
|
|
|
regularclean = True
|
|
|
|
ORD = 0
|
|
|
|
try:
|
|
|
|
ORD = ord(win.current["key_letter"])
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
backremove = False # Whether to make selection go to 0 width thing
|
|
|
|
#print(ORD, win.text[name]["cursor"][0])
|
|
|
|
|
|
|
|
|
|
|
|
# Backspace
|
|
|
|
if 65288 in win.current["keys"]:
|
|
|
|
if win.text[name]["cursor"][0] != 0 and win.text[name]["cursor"][0]\
|
|
|
|
== win.text[name]["cursor"][1]:
|
|
|
|
|
|
|
|
win.text[name]["text"] = win.text[name]["text"]\
|
|
|
|
[:win.text[name]["cursor"][0]-1]+\
|
|
|
|
win.text[name]["text"]\
|
|
|
|
[win.text[name]["cursor"][1]:]
|
|
|
|
|
|
|
|
elif win.text[name]["cursor"][1] != 0 and win.text[name]["cursor"][0]\
|
|
|
|
!= win.text[name]["cursor"][1]:
|
|
|
|
|
|
|
|
win.text[name]["text"] = win.text[name]["text"]\
|
|
|
|
[:win.text[name]["cursor"][0]]+\
|
|
|
|
win.text[name]["text"]\
|
|
|
|
[win.text[name]["cursor"][1]:]
|
|
|
|
backremove = True
|
|
|
|
|
|
|
|
# Ctrl - C
|
|
|
|
elif ORD == 3:
|
|
|
|
clipboard.set_text( win.text[name]["text"]\
|
|
|
|
[win.text[name]["cursor"][0]:win.text[name]["cursor"][1]], -1)
|
|
|
|
|
|
|
|
# Ctrl - V
|
|
|
|
elif ORD == 22:
|
|
|
|
|
|
|
|
cliptext = str(clipboard.wait_for_text())
|
|
|
|
|
|
|
|
win.text[name]["text"] = win.text[name]["text"]\
|
|
|
|
[:win.text[name]["cursor"][0]]\
|
|
|
|
+ cliptext +\
|
|
|
|
win.text[name]["text"]\
|
|
|
|
[win.text[name]["cursor"][1]:]
|
|
|
|
win.text[name]["cursor"][0] = win.text[name]["cursor"][1]
|
|
|
|
|
|
|
|
# Ctrl - A
|
|
|
|
elif ORD == 1:
|
|
|
|
win.text[name]["cursor"][0] = 0
|
|
|
|
win.text[name]["cursor"][1] = len(win.text[name]["text"])
|
|
|
|
|
|
|
|
# To clear up the Controll
|
|
|
|
elif 65507 in win.current["keys"]:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Shift
|
|
|
|
elif 65506 in win.current["keys"]:
|
|
|
|
# Right
|
|
|
|
if 65363 in win.current["keys"]:
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][1] + 1
|
|
|
|
#win.current["keys"].remove(65363)
|
|
|
|
|
|
|
|
# Left
|
|
|
|
elif 65361 in win.current["keys"]:
|
|
|
|
if win.text[name]["cursor"][1] > win.text[name]["cursor"][0]:
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][1] - 1
|
|
|
|
#win.current["keys"].remove(65361)
|
|
|
|
|
|
|
|
# Right button
|
|
|
|
elif 65363 in win.current["keys"]:
|
|
|
|
win.text[name]["cursor"][0] = win.text[name]["cursor"][0] + 1
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][0]
|
|
|
|
win.current["keys"].remove(65363)
|
|
|
|
|
|
|
|
# Left button
|
|
|
|
elif 65361 in win.current["keys"]:
|
|
|
|
win.text[name]["cursor"][0] = win.text[name]["cursor"][0] - 1
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][0]
|
|
|
|
win.current["keys"].remove(65361)
|
|
|
|
|
|
|
|
# Escape
|
|
|
|
elif 65307 in win.current["keys"]:
|
|
|
|
win.textactive = ""
|
|
|
|
win.current["keys"].remove(65307)
|
|
|
|
|
|
|
|
else:
|
|
|
|
win.text[name]["text"] = win.text[name]["text"]\
|
|
|
|
[:win.text[name]["cursor"][0]]\
|
|
|
|
+ win.current["key_letter"]+\
|
|
|
|
win.text[name]["text"]\
|
|
|
|
[win.text[name]["cursor"][1]:]
|
|
|
|
|
|
|
|
|
|
|
|
# Auto moving the cursor
|
|
|
|
nowlen = len(win.text[name]["text"])
|
|
|
|
if win.text[name]["cursor"][0] == win.text[name]["cursor"][1]:
|
|
|
|
win.text[name]["cursor"][0] = win.text[name]["cursor"][0] + (nowlen - prevlen)
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][0]
|
|
|
|
|
|
|
|
elif backremove:
|
|
|
|
win.text[name]["cursor"][1] = win.text[name]["cursor"][0]
|
|
|
|
|
|
|
|
|
|
|
|
if nowlen != prevlen and regularclean:
|
|
|
|
# Deleting all the keys from the keys. So yeah.
|
|
|
|
win.current["keys"] = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Outputing to the outlayer.
|
|
|
|
outlayer.set_source_surface(surface, x, y)
|
|
|
|
outlayer.paint()
|
|
|
|
|
|
|
|
# Button if editable.
|
|
|
|
if editable:
|
|
|
|
def do():
|
|
|
|
win.textactive = name
|
|
|
|
roundrect(outlayer, win,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
10,
|
|
|
|
fill=False,
|
2020-12-10 23:14:48 +01:00
|
|
|
button=do,
|
2020-12-11 18:21:19 +01:00
|
|
|
tip=tip,
|
2021-01-11 21:50:20 +01:00
|
|
|
offset=offset,
|
|
|
|
text=True)
|
2020-11-26 16:32:28 +01:00
|
|
|
outlayer.stroke()
|
|
|
|
|
|
|
|
if win.textactive == name:
|
|
|
|
UI_color.set(outlayer, win, "button_active")
|
|
|
|
roundrect(outlayer, win,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
10,
|
|
|
|
fill=False)
|
|
|
|
outlayer.stroke()
|
|
|
|
|