All pastes #1059593 Raw Edit

timmeh

public python v1 · immutable
#1059593 ·published 2008-07-01 01:39 UTC
rendered paste body
#Todo#-----##Give windows Z-depth so that they fade into the background in order of focus.#I think this can easily be done by list splicing and appends - DONEfrom Blender.BGL import *import Blenderfrom math import sin, cos, pifrom statictype import static_castfrom Blender.Draw import *class colour:	grey_20 = 		(0.2, 0.2, 0.2, 1.0)	grey_40 = 		(0.4, 0.4, 0.4, 1.0)	grey_60 = 		(0.6, 0.6, 0.6, 1.0)	grey_80 = 		(0.8, 0.8, 0.8, 1.0)	white =			(1.0, 1.0, 1.0, 1.0)	black =			(0.0, 0.0, 0.0, 1.0)	dark_grey = 	(0.3, 0.3, 0.3, 1.0)	mid_grey = 		(0.5, 0.5, 0.5, 1.0)	light_grey =	(0.7, 0.7, 0.7, 1.0)	orange =		(1.0, 0.5, 0.0, 1.0)	darkorange =	(0.8, 0.35, 0.0, 1.0)	selected =		grey_20	connector =		grey_20	text =			grey_60	list =			grey_60 	background = 	grey_40										#form background colour	grid =			dark_grey									#grid colour	window =		(0.2, 0.2, 0.2, 0.75)						#base window colour	windowsel =		(0.2, 0.2, 0.2, 0.5)						#selected window colour	titlebar =		dark_grey									#base title bar colour	titlebarsel =	(0.4, 0.4, 0.4, 0.75)						#selected title bar colour	outline =		grey_20										#window outline colour	outlinesel =	mid_grey									#selected window outline colour	titletext =		grey_60										#window title text colour	titletextsel =	grey_20										#selected window title text colour	button =		grey_40	buttonsel =		grey_20	buttonoutline = grey_20	buttonoutsel =	grey_20	buttontext =	grey_20	buttontextsel = grey_60			def get_window_size():											#gets almost correct window size	box = Buffer(GL_INT, 4)										#still needs offset correction	glGetIntegerv(GL_SCISSOR_BOX, box)							#better than Blender.Window.GetAreaSize	return boxdef get_mouse_coords():											#gets the mouse coordinates in local space	x, y, w, h = get_window_size()	xm, ym = Blender.Window.GetMouseCoords()	return xm - x, ym - ydef offset_correct():											#will be used for offsetting the blender header	return 0def constrain_canvas(x, y, w, h):	glEnable(GL_SCISSOR_TEST)	glPushAttrib(GL_VIEWPORT)									#save the viewport	glPushAttrib(GL_SCISSOR_BIT)				glMatrixMode(GL_PROJECTION)								glPushMatrix()												#save the projection matrix	glLoadIdentity()											#return proj matrix to identity		offset = offset_correct()									#correct header offset problems	glOrtho(0, w, 0 + offset, h, -1.0, 1.0)						#set coordinate system	glScissor(x, y, w, h)		glViewport(x, y, w, h)	def unconstrain_canvas():	glPopAttrib()												#return scissor box to old state	glPopAttrib()												#return viewport box to old state	glPopMatrix()												#return proj matrix to old statedef no_event():													#simple no event for buttons	print 'No event'	return@static_cast(int, int)def glScaledRasterPos2i(x, y):	viewMatrix = Buffer(GL_FLOAT, 16)							#space for modelview matrix	glGetFloatv(GL_MODELVIEW_MATRIX, viewMatrix)				#get modelview	f = 1/viewMatrix[0]											#get scale factor	glRasterPos2f(x*f, y*f) 									#instead of the usual glRasterPos2i(x, y)def glLine(x1, y1, x2, y2, col=None):							#draw a line between two points	if col:		glColor4f(*col)	glBegin(GL_LINES)	glVertex2i(x1,y1)	glVertex2i(x2,y2)	glEnd()def glBox(x1, y1, x2, y2, col=None, type=GL_POLYGON):			#draw a box between bottom-left (x1,y1)...	if col:														#and top-right (x2, y2)		glColor4f(*col)	glBegin(type)	glVertex2i(x1,y1)	glVertex2i(x2,y1)	glVertex2i(x2,y2)	glVertex2i(x1,y2)	glEnd()def glArc(x, y, rad, beg=0, end=90, steps=12):					#draw an arc - used by glRoundedBox...	beg = pi / 180 * beg										#to round off the corners	end = pi / 180 * end	ang_r = end - beg	ang_per_step = ang_r / steps	for step in range(steps):		xc = x + rad * cos(ang_per_step * step + beg)		yc = y + rad * sin(ang_per_step * step + beg)		glVertex2f(xc, yc)def glRoundedBox(x1, y1, x2, y2, rad=5, col=None, type=GL_POLYGON, flags=15):	if col:		glColor4f(*col)	if y1 > y2: y1, y2 = y2, y1									#swap the y coords if y1 is bigger than y2			glBegin(type)	if flags & 1 == 1:		glArc(x1 + rad, y1 + rad, rad, 180, 270)	else:		glVertex2i(x1, y1)	if flags & 2 == 2:		glArc(x2 - rad, y1 + rad, rad, 270, 360)	else:		glVertex2i(x2, y1)	if flags & 4 == 4:		glArc(x2 - rad, y2 - rad, rad, 0, 90)	else:		glVertex2i(x2, y2)	if flags & 8 == 8:		glArc(x1 + rad, y2 - rad, rad, 90, 180)	else:		glVertex2i(x1, y2)	glEnd()	def glInsetBox(x1, y1, x2, y2, inset=1):	glBox(x1, y1, x2, y2, colour.grey_20)	glBox(x1 + inset, y1, x2, y2 - inset, colour.grey_60)	glBox(x1 + inset, y1 + inset, x2 - inset, y2 - inset, colour.grey_40)def glOutsetBox(x1, y1, x2, y2, outset=1):	glBox(x1, y1, x2, y2, colour.grey_60)	glBox(x1 + outset, y1, x2, y2 - outset, colour.grey_20)	glBox(x1 + outset, y1 + outset, x2 - outset, y2 - outset, colour.grey_40)	class Element:		def __init__(self, x, y, w, h, visible):		self.visible = True										#state variables		self.x1 = x		self.y1 = y		self.x2 = x + w		self.y2 = y + h		self.h = h		self.w = w		self.dx = self.dy = 0		self.parent = None			def absolute_coordinates(self):								#this recurses through the hierarchy to		if self.parent:											#get the absolute coordinates for a particular			x, y = self.parent.absolute_coordinates()			#elements so that we can do hit testing etc.			return self.x1 + x, self.y1 + y		else:			return self.x1, self.y1			def set_focus(self, element):								#routine to set z level based on focus		self.elements.remove(element)							#not all elements will be able to use this		self.elements.extend([element])class Layer(Element):	'''Standard interface layer'''		@static_cast(None, int, int, int)							#make everything integers for opengl	def __init__(self, x, y, w, h):		Element.__init__(self, x, y, w, h, True)		self.elements = []										#store ui elements		def draw(self):								if not self.visible: return	False						#return if the layer isnt drawn (maybe used for caching later)		glEnable(GL_SCISSOR_TEST)								#enable scissor testing		glPushAttrib(GL_SCISSOR_BIT)							#save the old scissor box		glScissor(self.x1, self.y1, self.w, self.h)				#constrain drawing to layer bounds		for element in self.elements:							#draw all elements			element.draw()		glPopAttrib()											#return scissor box to old state				def link(self, element):									#link an element to the layer		element.parent = self		self.elements.append(element)	def event(self, evt, val):		x, y = Interface.mloc									#make a reversed copy of the element list for events...		evtlist = self.elements[:]								#since the top item is the last in the list		evtlist.reverse()		for element in evtlist:										x1, y1 = element.absolute_coordinates()			x2, y2 = x1 + element.w, y1 + element.h			if x1 < x < x2 and y1 < y < y2:						#if mouse coords are inside the element pass to the element...				handled = element.event(evt, val)				#event handler				if handled:					return True		return False					class GridLayer(Layer):	'''Standard interface layer with grid background'''		@static_cast(None, int, int, int, int)						#make everything integers for opengl	def __init__(self, x, y, w, h, step):					self.step = step										#step variable for distance between lines		Layer.__init__(self, x, y, w, h)		def draw(self):		if not self.visible: return	False						#return if the layer isnt drawn (maybe used for caching later)			#glDisable(GL_LINE_SMOOTH)								#straight lines so no AA needed		for y in range(self.y1, self.y2, self.step):			#draw grid lines			glLine(self.x1, y, self.x2, y, colour.grid)			for x in range(self.x1, self.x2, self.step):			glLine(x, self.y1, x, self.y2, colour.grid)		#glEnable(GL_LINE_SMOOTH)								#reenable AA for lines		Layer.draw(self)										#call to superclass draw method to draw everything else		class Window(Element):		@static_cast(None, int, int, int, int, title=str, visible=bool, radius=int)	def __init__(self, x, y, w, h, title='Window', visible=True, radius=10):		Element.__init__(self, x, y, w, h, True)		self.title = title										#title of the window		self.title_height = 24									#titelbar height		self.has_title = True									#does it have a titlebar		self.radius = radius									#radius of corners		self.elements = []										#elements of the window		self.sel = False										#is it currently selected				def draw(self):		#glDisable(GL_LINE_SMOOTH)								#smoothed lines look bad for this 		if self.has_title:										#draw the window itself with title bar			titlecol = colour.titlebar if not self.sel else colour.titlebarsel		#set colours depending...			outcol = colour.outline if not self.sel else colour.outlinesel			#on selection status			wincol = colour.window if not self.sel else colour.windowsel			glRoundedBox(self.x1, self.y1, self.x2, self.y2 - self.title_height, self.radius, wincol, type=GL_POLYGON, flags=3)			glRoundedBox(self.x1, self.y2 - self.title_height, self.x2, self.y2, self.radius, titlecol, type=GL_POLYGON, flags=12)			glLine(self.x1, self.y2 - self.title_height, self.x2, self.y2 - self.title_height, outcol)			if self.sel:				glColor4f(*colour.titletextsel)			else:				glColor4f(*colour.titletext)			glScaledRasterPos2i(self.x1 + 16, self.y2 - self.title_height + 6)			Blender.Draw.Text(self.title, 'large')		else:													#draw the window itself without title bar			glRoundedBox(self.x1, self.y1, self.x2, self.y2, self.radius, colour.window, type=GL_POLYGON)		glRoundedBox(self.x1, self.y1, self.x2, self.y2, self.radius, outcol, type=GL_LINE_LOOP)		#glEnable(GL_LINE_SMOOTH)								#reenable AA line		constrain_canvas(self.x1, self.y1, self.w, self.h - self.title_height) #limit drawing to this window		for element in self.elements:							#draw all elements			element.draw()		unconstrain_canvas()									#allow drawing on parent			def event(self, evt, val):		x, y = Interface.mloc									#mouse loc		ax, ay = self.absolute_coordinates()					#absolute coords of element		rx, ry = x - ax, y - ay									#relative coords		if evt == LEFTMOUSE and val == 1:						#left mouse button down			if 0 < rx < self.w and self.h - self.title_height < ry < self.h:	#if clicked on the titlebar...				self.parent.set_focus(self)						#set the focus to the current window				self.sel = True									#then set selectection status and ...				Interface.focus = self							#main interface focus and...				self.dx = x - self.x1							#set deltas for moving element				self.dy = y - self.y1			else:												#if clicked elsewhere then...				for element in self.elements:					#check which element to pass the event to					if element.x1 < rx < element.x2 and element.y1 < ry < element.y2:						handled = element.event(evt, val)						if handled:							self.parent.set_focus(self)			#set the focus to the current window							return True				return False		elif evt == LEFTMOUSE and val == 0:						#left mouse button up			self.sel = False			if Interface.focus is self: Interface.focus = None		elif evt == MOUSEX or evt == MOUSEY:					#mouse move			if self.sel:				self.x1 = -self.dx + x				self.y1 = -self.dy + y				self.x2 = self.x1 + self.w				self.y2 = self.y1 + self.h	def link(self, element):									#link element to window		element.parent = self		self.elements.append(element)		class Button(Element):		@static_cast(int, int, int, int, str, None, visible=bool, radius=int)	def __init__(self, x, y, w, h, text, event, visible=True, radius=10):		Element.__init__(self, x, y, w, h, visible)		self.text = text										#button text		self.evt = event										#callback		self.radius = radius									#button radius		self.sel = 0											#selection status		def draw(self):		if self.sel:											#button down			glRoundedBox(self.x1, self.y1, self.x2, self.y2, col=colour.buttonsel, rad=self.radius)			glRoundedBox(self.x1, self.y1, self.x2, self.y2, col=colour.buttonoutsel, type=GL_LINE_LOOP, rad=self.radius)		else:													#button up			glRoundedBox(self.x1, self.y1, self.x2, self.y2, col=colour.button, rad=self.radius)			glRoundedBox(self.x1, self.y1, self.x2, self.y2, col=colour.buttonoutline, type=GL_LINE_LOOP, rad=self.radius)		x, y = self.absolute_coordinates()						#get button abs coords		constrain_canvas(x, y, self.w, self.h)					#set bounding box		width = Blender.Draw.GetStringWidth(self.text)			#calculate offset for centering x		offsetx = (self.w - width) / 2		height = 10												#calculate offset for centering y		offsety = (self.h - height)/ 2									glScaledRasterPos2i(offsetx, offsety)					#offset the text		tcol = colour.buttontext if not self.sel else colour.buttontextsel		glColor4f(*tcol)		Blender.Draw.Text(self.text)							#draw text		unconstrain_canvas()									#unset bounding box			def event(self, evt, val):		x, y = Interface.mloc									#mouse loc		ax, ay = self.absolute_coordinates()					#absolute coords of element		rx, ry = x - ax, y - ay									#relative coords		if evt == LEFTMOUSE and val == 1:			self.parent.set_focus(self)			self.sel = True			Interface.focus = self		elif evt == LEFTMOUSE and val == 0:			if 0 < rx < self.w and 0 < ry < self.h:				self.evt()			self.sel = 0			Interface.focus = None			return 1		elif evt == MOUSEX or evt == MOUSEY:			if 0 < rx < self.w and 0 < ry < self.h:				self.sel = 1			else:				self.sel = 0					class Interface(Element):	focus = None	mloc = [0,0]		def __init__(self):		self.elements = []										#storage for interface elements		x,y,w,h = get_window_size()				Element.__init__(self, x, y, w, h, True)		self.parent = None										#needed for recursive absolute coords			def draw(self):		x,y,w,h = get_window_size()		glShadeModel(GL_SMOOTH)									#set up gl state variables		glDisable(GL_LINE_SMOOTH)		glEnable(GL_BLEND)		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)		glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)		glClearColor(*colour.background)		glClear(GL_COLOR_BUFFER_BIT)		for element in self.elements:							#draw everything			element.draw()									def event(self, evt, val=0):		Interface.mloc = get_mouse_coords()		if Interface.focus:			handled = Interface.focus.event(evt, val)			if handled:				return		for element in self.elements:			handled = element.event(evt, val)			if handled:				return		Blender.Draw.Redraw()		def launch(self):		Blender.Draw.Register(self.draw, self.event, self.event)#start the interface		def link(self, element):									#link an element to the ui		element.parent = self		self.elements.append(element)x,y,w,h = get_window_size()box = Window(100, 100, 400, 300, 'Test Window', radius=10)box2 = Window(50, 50, 300, 200, 'Window 2', radius=15)but = Button(10, 50, 64, 24, 'Test', no_event)box.link(but)canvas = GridLayer(x, 0, w, h, 30)canvas.link(box)#canvas.link(box2)ui = Interface()ui.link(canvas)ui.launch()