Fancied up the text window with pango. Added lots of font options and

effects.

Fixed some bugs with resizing a window when it's not draggable and
resizing a window when the max size is (-1, -1)
This commit is contained in:
Joey Payne 2014-02-06 08:00:24 -07:00
commit 5854cfee86
3 changed files with 195 additions and 65 deletions

View file

@ -1,6 +1,8 @@
import cairo
import pango
import pangocairo as pc
import math
from .structures import Size, Position, Rectangle, Color, BorderRadius
from .structures import Size, Position, Rectangle, Color, BorderRadius, Padding
from ..events.events import WindowEventSource
debug = True
@ -71,10 +73,32 @@ class WindowSurface(object):
'butt': cairo.LINE_CAP_BUTT,
'square': cairo.LINE_CAP_SQUARE}
font_weights = {'bold': pango.WEIGHT_BOLD,
'normal': pango.WEIGHT_NORMAL,
'book': pango.WEIGHT_BOOK,
'heavy': pango.WEIGHT_HEAVY,
'light': pango.WEIGHT_LIGHT,
'medium': pango.WEIGHT_MEDIUM,
'semibold': pango.WEIGHT_SEMIBOLD,
'thin': pango.WEIGHT_THIN,
'ultrabold': pango.WEIGHT_ULTRABOLD,
'ultraheavy': pango.WEIGHT_ULTRAHEAVY,
'ultralight': pango.WEIGHT_ULTRALIGHT}
font_styles = {'italic': pango.STYLE_ITALIC,
'oblique': pango.STYLE_OBLIQUE,
'normal': pango.STYLE_NORMAL}
wrap_modes = {'word': pango.WRAP_WORD,
'char': pango.WRAP_CHAR,
'word_char': pango.WRAP_WORD_CHAR}
font_map = pc.cairo_font_map_get_default()
font_list = [f.get_name() for f in font_map.list_families()]
def __init__(self):
super(WindowSurface, self).__init__()
def draw_circle(self, position, size, color=(1,1,1,1), line_width=1.0, line_color=(0,0,0,1), start_angle=0.0, end_angle=360.0):
color = Color.from_value(color)
line_color = Color.from_value(line_color)
@ -100,27 +124,48 @@ class WindowSurface(object):
context.set_source_rgba(*line_color)
context.stroke()
def draw_text(self, text, position, size, color=(0,0,0,1), line_width=1.0, background_color=(1,1,1,0), fill_color=None):
color = Color.from_value(color)
def draw_text(self, text, position, font_size=12,
font_weight='normal',
font_style='normal', font_color=(0,0,0,1),
font_family='Sans', word_wrap='word',
alignment=pango.ALIGN_LEFT, line_width=1.0,
background_color=(1,1,1,0), fill_color=None):
color = Color.from_value(font_color)
background_color = Color.from_value(background_color)
position = Position.from_value(position)
size = Size.from_value(size)
context = self.context
width = size.width
height = size.height
font_weight = self.font_weights[font_weight]
font_style = self.font_styles[font_style]
pc_context = pc.CairoContext(context)
pc_context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
layout = pc_context.create_layout()
font = pango.FontDescription('{} {}'.format(font_family, font_size))
font.set_weight(font_weight)
font.set_style(font_style)
layout.set_font_description(font)
layout.set_text(text)
layout.set_wrap(self.wrap_modes[word_wrap])
width = self.size.width - (self.padding.left + self.padding.right)
layout.set_width(int(width*pango.SCALE))
layout.set_alignment(alignment)
context.set_line_width(line_width)
context.set_source_rgba(*color)
context.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
context.set_font_size(size.height)
context.set_source_rgba(*font_color)
#context.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
#context.set_font_size(size.height)
extents = context.text_extents(text)
x,y = (self.position.x+position.x+2,
self.position.y+position.y+extents[3]+2)
x,y = (self.position.x+position.x+self.padding.left,
self.position.y+position.y+self.padding.top)
context.move_to(x, y)
context.show_text(text)
pc_context.update_layout(layout)
pc_context.show_layout(layout)
#context.show_text(text)
def draw_lines(self, lines, line_color=(0,0,0,1), background_color=(1,1,1,1), line_width=1, line_join='miter', line_cap='butt'):
@ -245,19 +290,21 @@ class Window(WindowEventSource, WindowSurface):
position = Position.from_value(kwargs.pop('position', Position()))
size = Size.from_value(kwargs.pop('size', Size()))
self.context = kwargs.pop('context', None)
self.min_size = kwargs.pop('min_size', Size(1,1))
self.corner_handle_size = kwargs.pop('corner_handle_size', Size(20, 20))
self.min_size = Size.from_value(kwargs.pop('min_size', Size(1,1)))
self.max_size = Size.from_value(kwargs.pop('max_size', Size(-1,-1)))
self.corner_handle_size = Size.from_value(kwargs.pop('corner_handle_size', Size(20, 20)))
self.edge_handle_width = kwargs.pop('edge_handle_width', 10)
self.edge_handle_buffer = kwargs.pop('edge_handle_buffer', Size(5, 5))
self.edge_handle_buffer = Size.from_value(kwargs.pop('edge_handle_buffer', Size(5, 5)))
self.border_width = kwargs.pop('border_width', 1)
self.border_color = Color.from_value(kwargs.pop('border_color', (0,0,0,0)))
self.background_color = Color.from_value(kwargs.pop('background_color', (0,0,0,0)))
self.border_radius = BorderRadius.from_value(kwargs.pop('border_radius', 1))
self.padding = Padding.from_value(kwargs.pop('padding', 0))
self.dashed_border = kwargs.pop('dashed_border', False)
self.clip_children = kwargs.pop('clip_children', False)
self.ignore_debug = kwargs.pop('ignore_debug', False)
self.rectangle = Rectangle(position, size)
self.rectangle = Rectangle()
self.children = []
self.parent = None
self.mouse_pos = Position(size.width/2, size.height/2)
@ -269,6 +316,8 @@ class Window(WindowEventSource, WindowSurface):
self.focused = False
self.visible = True
self.accept('mouse-move', self.process_mouse_move)
self.size = size
self.position = position
self.resizable = kwargs.pop('resizable', self._resizable)
self.draggable = kwargs.pop('draggable', self._draggable)
@ -316,6 +365,25 @@ class Window(WindowEventSource, WindowSurface):
self.reject('mouse-left', self.click)
self.reject('mouse-left-up', self.click_up)
def _restrict_pos_size(self, new_pos, new_size):
if new_size.height <= self.min_size.height:
new_pos.y = self.position.y + self.size.height - self.min_size.height
new_size.height = self.min_size.height
if new_size.width <= self.min_size.width:
new_pos.x = self.position.x + self.size.width - self.min_size.width
new_size.width = self.min_size.width
if self.max_size.height > -1 and new_size.height >= self.max_size.height:
new_pos.y = self.position.y + self.size.height - self.max_size.height
new_size.height = self.max_size.height
if self.max_size.width > -1 and new_size.width >= self.max_size.width:
new_pos.x = self.position.x + self.size.width - self.max_size.width
new_size.width = self.max_size.width
return new_pos, new_size
def drag_bottomright_handle(self, obj, mouse_pos):
diff = mouse_pos - self.mouse_diff
old_size = Size.from_value(self.size)
@ -326,13 +394,16 @@ class Window(WindowEventSource, WindowSurface):
def drag_bottomleft_handle(self, obj, mouse_pos):
diff = mouse_pos - self.mouse_diff
old_size = Size.from_value(self.size)
new_pos = Position(self.position.x+diff.x, self.position.y)
new_pos = Position(self.position.x + diff.x, self.position.y)
new_size = Size(self.size.width - diff.x, self.size.height + diff.y)
if new_size.width <= self.min_size.width:
new_pos.x = self.position.x + self.size.width - self.min_size.width
new_size.width = self.min_size.width
self.position = new_pos
self.size = new_size
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
if self.draggable:
self.position = new_pos
self.size = new_size
else:
self.size = [self.size.width, new_size.height]
self.mouse_diff.y = obj.position.y + self.handle_diff.y
self.mouse_diff.x = obj.position.x + self.handle_diff.x
@ -342,12 +413,14 @@ class Window(WindowEventSource, WindowSurface):
old_size = Size.from_value(self.size)
new_pos = Position(self.position.x, self.position.y + diff.y)
new_size = Size(self.size.width + diff.x, self.size.height - diff.y)
if new_size.height <= self.min_size.height:
new_pos.y = self.position.y + self.size.height - self.min_size.height
new_size.height = self.min_size.height
self.position = new_pos
self.size = new_size
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
if self.draggable:
self.position = new_pos
self.size = new_size
else:
self.size = [new_size.width, self.size.height]
self.mouse_diff.y = obj.position.y + self.handle_diff.y
self.mouse_diff.x = obj.position.x + self.handle_diff.x
@ -357,15 +430,12 @@ class Window(WindowEventSource, WindowSurface):
old_size = Size.from_value(self.size)
new_pos = Position(self.position.x + diff.x, self.position.y + diff.y)
new_size = Size(self.size.width - diff.x, self.size.height - diff.y)
if new_size.height <= self.min_size.height:
new_pos.y = self.position.y + self.size.height - self.min_size.height
new_size.height = self.min_size.height
if new_size.width <= self.min_size.width:
new_pos.x = self.position.x + self.size.width - self.min_size.width
new_size.width = self.min_size.width
self.position = new_pos
self.size = new_size
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
if self.draggable:
self.position = new_pos
self.size = new_size
self.mouse_diff.y = obj.position.y + self.handle_diff.y
self.mouse_diff.x = obj.position.x + self.handle_diff.x
@ -375,11 +445,12 @@ class Window(WindowEventSource, WindowSurface):
old_size = Size.from_value(self.size)
new_pos = Position(self.position.x, self.position.y + diff.y)
new_size = Size(self.size.width, self.size.height - diff.y)
if new_size.height <= self.min_size.height:
new_pos.y = self.position.y + self.size.height - self.min_size.height
new_size.height = self.min_size.height
self.position = new_pos
self.size = new_size
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
if self.draggable:
self.position = new_pos
self.size = new_size
self.mouse_diff.y = obj.position.y + self.handle_diff.y
self.mouse_diff.x = obj.position.x + self.handle_diff.x
@ -389,11 +460,12 @@ class Window(WindowEventSource, WindowSurface):
old_size = Size.from_value(self.size)
new_pos = Position(self.position.x+diff.x, self.position.y)
new_size = Size(self.size.width - diff.x, self.size.height)
if new_size.width <= self.min_size.width:
new_pos.x = self.position.x + self.size.width - self.min_size.width
new_size.width = self.min_size.width
self.position = new_pos
self.size = new_size
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
if self.draggable:
self.position = new_pos
self.size = new_size
self.mouse_diff.x = obj.position.x + self.handle_diff.x
@ -705,7 +777,9 @@ class Window(WindowEventSource, WindowSurface):
def add_child(self, child_window):
if child_window not in self.children:
child_window.parent = self
child_window.position = child_window.position + self.position + [self.border_width/2, self.border_width/2]
child_window.position = child_window.position + self.position +\
[self.border_width/2, self.border_width/2] +\
[self.padding.left, self.padding.top]
child_window.context = self.context
self.children.append(child_window)
@ -737,10 +811,17 @@ class Window(WindowEventSource, WindowSurface):
@size.setter
def size(self, size):
size = Size.from_value(size)
if size.height <= self.min_size.height:
size.height = self.min_size.height
if size.width <= self.min_size.width:
size.width = self.min_size.width
if self.max_size.width > -1 and size.width >= self.max_size.width:
size.width = self.max_size.width
if self.max_size.height > -1 and size.height >= self.max_size.height:
size.height = self.max_size.height
diff = size - self.rectangle.size
if diff.height != 0 or diff.width != 0:
self.dispatch('resize', self, size)
@ -769,10 +850,20 @@ class Mouse(Window):
class TextWindow(Window):
def __init__(self, name, text, *args, **kwargs):
super(TextWindow, self).__init__(name, *args, **kwargs)
self.font_size = kwargs.pop('font_size', 12)
self.font_style = kwargs.pop('font_style', 'normal')
self.font_weight = kwargs.pop('font_weight', 'normal')
self.font_family = kwargs.pop('font_family', 'Sans')
self.word_wrap = kwargs.pop('word_wrap', 'word')
self.font_color = Color.from_value(kwargs.pop('font_color', (0,0,0,1)))
self.text = text
def render(self):
super(TextWindow, self).render()
self.draw_text(self.text, [0,0], self.size)
self.draw_text(self.text, [0,0],
self.font_size, font_weight=self.font_weight,
font_style=self.font_style, font_family=self.font_family,
font_color=self.font_color, word_wrap=self.word_wrap)