12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- # THIS FILE IS A PART OF VCStudio
- # PYTHON 3
- # This a console project manager.
- import os
- import math
- import urllib3
- import hashlib
- import threading
- # 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
- from settings import fileformats
- # UI
- from UI import UI_color
- def roundrect(layer, win, x, y, width, height, r, button=False, icon=False,
- tip="", fill=True, url="", clip=False, offset=[0,0], text=False):
-
- # 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:
-
- # So icons would not disappear LOL
- if icon:
- image(layer, win, "settings/themes/"\
- +win.settings["Theme"]+"/icons/"+icon+".png", x, y)
-
- 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()
-
-
- 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])) :
- 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:
-
- # cursor
- if text:
- win.current["cursor"] = win.cursors["text"]
- else:
- win.current["cursor"] = win.cursors["hand"]
-
- # 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"]:
- win.current["cursor"] = win.cursors["watch"]
- 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
-
- # 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"]
-
- # 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.
-
-
-
- # 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"]
-
- if time == 0:
- return v2
-
- # 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)
-
- # Pixelized Blur
- if not "PixelBlur" in win.settings:
- settings.write("PixelBlur", False) # Writing to file
- win.settings = settings.load_all()
- if win.settings["PixelBlur"]:
- p = slaceuplayer.get_source()
- p.set_filter(cairo.FILTER_NEAREST)
- slaceuplayer.paint()
-
- return surface2
- else:
- return surface
- 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):
- # 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("/","_")
- # It could an image data without an image .png thing
- found = False
- for f in fileformats.images:
- if filename.endswith(f):
- found = True
- if not found:
- filename = filename+".png"
- 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
-
- # 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.
-
- # 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:
-
- 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")
-
- # 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:
- 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")
- # 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:
-
- 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")
-
- if not foundformat:
-
- # If you can't find any format. Just use the file icon then
- 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")
-
-
- # 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)
- # 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)
-
-
- # Let's make an ability for theme makers to simply color the standard
- # icons into any shade.
-
- imagedraw.set_source_surface(load2, dx, dy)
- imagedraw.paint()
- else:
- imagesurface = load2
-
- # Saving it into the win.images
- if tmppath:
- path = tmppath
- 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
- def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0, offset=[0,0]):
-
- # 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.
-
- if cell not in win.images:
- win.images[cell] = {}
-
- if int(x) not in range(int(0-width ), int(win.current["w"])) or \
- int(y) not in range(int(0-height-win.current["h"]), int(win.current["h"])) :
- return
-
- # If you ran this software you probably noticed that images are loading
- # dynamically. I did it using this following crazy algorythm borowed from
- # 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.
-
-
- # 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")
-
- if path not in win.images[cell] or win.images[cell][path]["loading"]:
-
- # If this is the first time this image is draw we want to create it a data
- # structure. So we could load the image.
-
- 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.
-
- t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit, cell))
- t.start()
-
- #loading it back
- else:
- if win.images[cell][path]["image"] and win.images[cell][path]["image"] != "download_button":
- imagesurface = win.images[cell][path]["image"]
-
- # Writting the image to the screen
-
-
- 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
-
- # 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()
-
- 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)
-
- 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,
- url="", strenght=50, bar_always=False):
-
- # 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
-
- x = int(x)
- y = int(y)
- width = int(width)
- height = int(height)
-
- # 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:
-
- amount = win.current["scroll"][1]*strenght
-
-
- if mmb_only:
- mmb = True
-
- # Middle mouse button scroll, or using a graphical tablet.
- if mmb:
- if win.current["MMB"]:
- if not sideways:
- amount = 0 - ( win.current["my"] - win.previous["my"] )
- else:
- amount = 0 - ( win.current["mx"] - win.previous["mx"] )
-
- # 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
-
- 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
- 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
- fraction = 1
-
- # 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.
-
- if not tobreak or bar_always:
-
- # 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)) \
- and int(win.current['LMB'][1]) in range(int(y), int(y+(height-60))) :
-
- 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
- )
- 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
- fraction = 1
-
- # 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.
-
- if not tobreak or bar_always:
-
- # 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
- )
-
- # 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
- )
- def text(outlayer, win, name, x, y, width, height, set_text="", parse=False, fill=True,
- editable=True, multiline=False , linebreak=False, centered=False, tip="",
- offset=[0,0]):
-
- # 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
- 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)
- 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
- while cursor2location > width - 5:
- 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")
- 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
- )
- 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()
- elif win.text[name]["cursor"][0] != win.text[name]["cursor"][1]:
- 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"]:
- 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)
-
- # 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
- if 65293 in win.current["keys"] or 65421 in win.current["keys"]\
- or 65289 in win.current["keys"]: # TAB
- win.current["key_letter"] = ""
-
-
- 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,
- button=do,
- tip=tip,
- offset=offset,
- text=True)
- 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()
|