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:
parent
3fe1e674fb
commit
5854cfee86
3 changed files with 195 additions and 65 deletions
|
|
@ -117,6 +117,8 @@ class Size(StructureBase):
|
|||
return Size(self.width*other, self.height*other)
|
||||
|
||||
def area(self):
|
||||
if self.width < 0 or self.height < 0:
|
||||
return -1
|
||||
return self.width*self.height
|
||||
|
||||
def __iter__(self):
|
||||
|
|
@ -124,21 +126,7 @@ class Size(StructureBase):
|
|||
yield self.height
|
||||
|
||||
|
||||
class BorderRadius(StructureBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
values = self._parse_value(args)
|
||||
self.topleft = kwargs.get('topleft', values[0])
|
||||
self.topright = kwargs.get('topright', values[1])
|
||||
self.bottomright = kwargs.get('bottomright', values[2])
|
||||
self.bottomleft = kwargs.get('bottomleft', values[3])
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
yield self.topleft
|
||||
yield self.topright
|
||||
yield self.bottomright
|
||||
yield self.bottomleft
|
||||
|
||||
class FourValueStructure(StructureBase):
|
||||
@classmethod
|
||||
def _parse_value(cls, value):
|
||||
f = lambda *args: args
|
||||
|
|
@ -161,6 +149,36 @@ class BorderRadius(StructureBase):
|
|||
return cls(*cls._parse_value(value))
|
||||
|
||||
|
||||
class BorderRadius(FourValueStructure):
|
||||
def __init__(self, *args, **kwargs):
|
||||
values = self._parse_value(args)
|
||||
self.topleft = kwargs.get('topleft', values[0])
|
||||
self.topright = kwargs.get('topright', values[1])
|
||||
self.bottomright = kwargs.get('bottomright', values[2])
|
||||
self.bottomleft = kwargs.get('bottomleft', values[3])
|
||||
|
||||
def __iter__(self):
|
||||
yield self.topleft
|
||||
yield self.topright
|
||||
yield self.bottomright
|
||||
yield self.bottomleft
|
||||
|
||||
|
||||
class Padding(FourValueStructure):
|
||||
def __init__(self, *args, **kwargs):
|
||||
values = self._parse_value(args)
|
||||
self.top = kwargs.get('top', values[0])
|
||||
self.right = kwargs.get('right', values[1])
|
||||
self.bottom = kwargs.get('bottom', values[2])
|
||||
self.left = kwargs.get('left', values[3])
|
||||
|
||||
def __iter__(self):
|
||||
yield self.top
|
||||
yield self.right
|
||||
yield self.bottom
|
||||
yield self.left
|
||||
|
||||
|
||||
class Color(StructureBase):
|
||||
def __init__(self, r=0, g=0, b=0, a=1):
|
||||
self.r = float(r)
|
||||
|
|
@ -265,6 +283,26 @@ class Rectangle(StructureBase):
|
|||
def size(self, size):
|
||||
self._size = Size.from_value(size)
|
||||
|
||||
def contains(self, other):
|
||||
other = Rectangle.from_value(other)
|
||||
if self.intersects_with(other.position) and\
|
||||
self.intersects_with(other.position + other.size - [1,1]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def intersection(self, other):
|
||||
other = Rectangle.from_value(other)
|
||||
if other.contains(self):
|
||||
return self
|
||||
elif self.contains(other):
|
||||
return other
|
||||
if self.intersects_with(other.position):
|
||||
return Rectangle(other.position, self.position + self.size - other.position)
|
||||
elif other.intersects_with(self.position):
|
||||
return Rectangle(self.position, other.position + other.size - self.position)
|
||||
else:
|
||||
return Rectangle()
|
||||
|
||||
def intersects_with(self, position):
|
||||
"""Checks if the position is within the bounds of the rectangle including the edges"""
|
||||
pos = Position.from_value(position)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class TestSurface(Surface):
|
|||
my_win = Window('child',
|
||||
position=[0,0],
|
||||
size=[500,200],
|
||||
max_size=[600, -1],
|
||||
draggable=True,
|
||||
resizable=True,
|
||||
min_size=Size(40,40),
|
||||
|
|
@ -16,11 +17,11 @@ class TestSurface(Surface):
|
|||
background_color=(1,1,1),
|
||||
clip_children=True,
|
||||
ignore_debug=True)
|
||||
my_win.add_child(TextWindow('text','Micro Bean.', position=[20,20], size=[100, 50]))
|
||||
my_win.add_child(TextWindow('text','Micro Bean is the best bean ever.', position=[20,20], size=[100, 50], resizable=True, clip_children=True, font_color=(1,0,1), padding=20, draggable=True))
|
||||
self.root_window.add_child(my_win)
|
||||
self.root_window.add_child(TestWindow('child2', position=[200,200], size=[200,200], draggable=True))
|
||||
child3 = TestWindow('child3', position=[300,300], size=[200,200], draggable=True)
|
||||
child3.add_child(TestWindow('child3-1', position=[10,10], size=[50,50], draggable=True))
|
||||
child3.add_child(TestWindow('child3-1', position=[10,10], size=[50,50], draggable=True, resizable=True))
|
||||
self.root_window.add_child(child3)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue