Source code for peng3d.gui.text

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  text.py
#  
#  Copyright 2016 notna <notna@apparat.org>
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#  

__all__ = [
    "Label",
    "TextInput","TextInputBackground"
    ]

import time

import pyglet
from pyglet.gl import *
from pyglet.window import key

from .widgets import Background,Widget,mouse_aabb

[docs]class Label(Widget): """ Simple widget that can display any single-line non-formatted string. This widget does not use any background by default. The default font color is chosen to work on the default background color and may need to be changed if the background color is changed. """ def __init__(self,name,submenu,window,peng, pos=None,size=None, bg=None, label="Label", font_size=16,font="Arial", font_color=[62,67,73,255], ): super(Label,self).__init__(name,submenu,window,peng,pos,size,bg) self._label = pyglet.text.Label(label, font_name=font, font_size=font_size, color=font_color, x=0,y=0, batch=self.submenu.batch2d, anchor_x="center", anchor_y="center", group=pyglet.graphics.OrderedGroup(1), width=self.size[0],height=self.size[1] ) self.redraw() def redraw(self,dt=None): super(Label,self).redraw() self.redraw_label() redraw.__noautodoc__ = True
[docs] def redraw_label(self): """ Re-draws the text by calculating its position. Currently, the text will always be centered on the position of the label. """ # Convenience variables sx,sy = self.size x,y = self.pos # Label position self._label.x = x+sx/2. self._label.y = y+sy/2. self._label.width = self.size[0] self._label.height = self.size[1] self._label._update() # Needed to prevent the label from drifting to the top-left after resizing by odd amounts
@property def label(self): """ Property for accessing the text of the label. """ return self._label.text @label.setter def label(self,label): self._label.text = label
[docs]class TextInputBackground(Background): """ Background for the :py:class:`TextInput` Widget. This background uses the button drawing routines and adds a cursor. """ def __init__(self,widget,border,borderstyle="flat"): self.border = border self.borderstyle = borderstyle self.stime = 0 super(TextInputBackground,self).__init__(widget) def init_bg(self): self.vlist = self.submenu.batch2d.add(20,GL_QUADS,pyglet.graphics.OrderedGroup(1), "v2f", "c3B", ) self.vlist_cursor = self.submenu.batch2d.add(2,GL_LINES,pyglet.graphics.OrderedGroup(10), "v2f", "c3B", ) def redraw_bg(self): # Convenience variables sx,sy = self.widget.size x,y = self.widget.pos bx,by = self.border # Button background # Outer vertices # x y v1 = x, y+sy v2 = x+sx, y+sy v3 = x, y v4 = x+sx, y # Inner vertices # x y v5 = x+bx, y+sy-by v6 = x+sx-bx, y+sy-by v7 = x+bx, y+by v8 = x+sx-bx, y+by # 5 Quads, for edges and the center qb1 = v5+v6+v2+v1 qb2 = v8+v4+v2+v6 qb3 = v3+v4+v8+v7 qb4 = v3+v7+v5+v1 qc = v7+v8+v6+v5 v = qb1+qb2+qb3+qb4+qc self.vlist.vertices = v bg = self.submenu.bg[:3] if isinstance(self.submenu.bg,list) or isinstance(self.submenu.bg,tuple) else [242,241,240] o,i = bg, [min(bg[0]+8,255),min(bg[1]+8,255),min(bg[2]+8,255)] s,h = [max(bg[0]-40,0),max(bg[1]-40,0),max(bg[2]-40,0)], [min(bg[0]+12,255),min(bg[1]+12,255),min(bg[2]+12,255)] # Outer,Inner,Shadow,Highlight if self.borderstyle == "flat": # Flat style makes no difference between normal,hover and pressed cb1 = i+i+i+i cb2 = i+i+i+i cb3 = i+i+i+i cb4 = i+i+i+i cc = i+i+i+i elif self.borderstyle == "gradient": if self.widget.focussed: i = s elif self.widget.is_hovering: i = [min(i[0]+6,255),min(i[1]+6,255),min(i[2]+6,255)] cb1 = i+i+o+o cb2 = i+o+o+i cb3 = o+o+i+i cb4 = o+i+i+o cc = i+i+i+i elif self.borderstyle == "oldshadow": if self.widget.focussed: i = s s,h = h,s elif self.widget.is_hovering: i = [min(i[0]+6,255),min(i[1]+6,255),min(i[2]+6,255)] s = [min(s[0]+6,255),min(s[1]+6,255),min(s[2]+6,255)] cb1 = h+h+h+h cb2 = s+s+s+s cb3 = s+s+s+s cb4 = h+h+h+h cc = i+i+i+i elif self.borderstyle == "material": if self.widget.focussed: i = [max(bg[0]-20,0),max(bg[1]-20,0),max(bg[2]-20,0)] elif self.widget.is_hovering: i = [max(bg[0]-10,0),max(bg[1]-10,0),max(bg[2]-10,0)] cb1 = s+s+o+o cb2 = s+o+o+s cb3 = o+o+s+s cb4 = o+s+s+o cc = i+i+i+i else: raise ValueError("Invalid Border style") c = cb1+cb2+cb3+cb4+cc self.vlist.colors = c # TODO: make this less hacky otext = self.widget._text.text self.widget._text.text = self.widget._text.text[:self.widget.cursor_pos] tw = self.widget._text.content_width+2 if len(self.widget._text.text)!=0 else 0 self.widget._text.text = otext v = x+tw+bx,y+by, x+tw+bx,y+sy-by c = i*2 if (self.stime-time.time())%1>.5 or not self.widget.focussed else [0,0,0]*2 self.vlist_cursor.vertices = v self.vlist_cursor.colors = c
[docs]class TextInput(Widget): """ Basic Textual Input widget. By default, this widget uses :py:class:`TextInputBackground` as its Background class. The optional default text will only be displayed if the text is empty. The ``allow_overflow`` flag determines if the text entered can be longer than the size of the :py:class:`TextInput`\ . """ def __init__(self,name,submenu,window,peng, pos=None,size=None, bg=None, text="",default="", border=[4,4],borderstyle="flat", font_size=16,font="Arial", font_color=[62,67,73,255], font_color_default=[62,67,73,200], allow_overflow=False ): if bg is None: bg = TextInputBackground(self,border,borderstyle) super(TextInput,self).__init__(name,submenu,window,peng,pos,size,bg) self.cursor_pos = 0 self.focussed = False self.allow_overflow = allow_overflow self._text = pyglet.text.Label(text, font_name=font, font_size=font_size, color=font_color, x=0,y=0, batch=None,#self.submenu.batch2d, anchor_x="left", anchor_y="center", width=self.size[0],height=self.size[1] ) self._default = pyglet.text.Label(default, font_name=font, font_size=font_size, color=font_color_default, x=0,y=0, batch=None, anchor_x="left", anchor_y="center", width=self.size[0],height=self.size[1] ) self.cursor_pos = 0 self.focussed = False self.peng.registerEventHandler("on_text",self.on_text) self.peng.registerEventHandler("on_text_motion",self.on_text_motion) self.redraw() pyglet.clock.schedule_interval(self.redraw,1./2.) def redraw(self,dt=None): super(TextInput,self).redraw() self.redraw_label() redraw.__noautodoc__ = True
[docs] def redraw_label(self): """ Re-draws the label by calculating its position. Currently, the label will always be centered on the position of the label. """ # Convenience variables sx,sy = self.size x,y = self.pos # Label position x = x+self.bg.border[0] y = y+sy/2.-self._text.font_size/4. w = self.size[0] h = self.size[1] self._text.x,self._text.y = x,y self._text.width,self._text.height=w,h self._default.x,self._default.y = x,y self._default.width,self._default.height=w,h self._text._update() # Needed to prevent the label from drifting to the top-left after resizing by odd amounts self._default._update()
def draw(self): super(TextInput,self).draw() if self._text.text=="": self._default.draw() else: self._text.draw() draw.__noautodoc__ = True def on_text(self,text): if not (self.focussed and self.clickable): return t = self.text t = t[:self.cursor_pos]+text+t[self.cursor_pos:] self.text = t self.cursor_pos+=len(text) self.cursor_pos = min(self.cursor_pos,len(self.text)) self.redraw() def on_text_motion(self,motion): if not (self.focussed and self.clickable): return if motion == key.MOTION_BACKSPACE: self.text = self.text[:-1] self.cursor_pos-=1 self.cursor_pos = max(self.cursor_pos,0) self.redraw() elif motion == key.MOTION_LEFT: self.cursor_pos-=1 self.cursor_pos = max(self.cursor_pos,0) self.redraw() elif motion == key.MOTION_RIGHT: self.cursor_pos+=1 self.cursor_pos = min(self.cursor_pos,len(self.text)) self.redraw() elif motion == key.MOTION_BEGINNING_OF_LINE: self.cursor_pos=0 self.redraw() elif motion == key.MOTION_END_OF_LINE: self.cursor_pos = len(self.text) self.redraw() def on_mouse_press(self,x,y,button,modifiers): if not self.clickable: return elif mouse_aabb([x,y],self.size,self.pos): if button == pyglet.window.mouse.LEFT: self.doAction("press") self.pressed = True self.focussed = True self.bg.stime = time.time() elif button == pyglet.window.mouse.RIGHT: self.doAction("context") self.redraw() else: self.focussed = False self.redraw() @property def text(self): """ Property for accessing the text. """ return self._text.text @text.setter def text(self,text): otext = self._text.text self._text.text = text self._text._update() if not self.allow_overflow and self.size[0]-self.bg.border[0]*2<=self._text.content_width if len(self.text)!=0 else 0: self._text.text=otext @property def default(self): """ Property for accessing the default text. """ return self._default.text @default.setter def default(self,default): self._default.text = default