Fixed some issues with image aspect ratio. Added preliminary gradient features for rounded rectangles.
This commit is contained in:
parent
b1396cd70a
commit
bed8281b22
5 changed files with 184 additions and 36 deletions
BIN
img/bluebanner.png
Normal file
BIN
img/bluebanner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
img/test.png
Normal file
BIN
img/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -224,7 +224,12 @@ class Color(StructureBase):
|
|||
if isinstance(hex_val, (long, int)):
|
||||
hexstring = "{0:x}".format(abs(hex_val))
|
||||
elif isinstance(hex_val, basestring):
|
||||
hexstring = hexval[2:]
|
||||
if hex_val.startswith('0x'):
|
||||
hexstring = hex_val[2:]
|
||||
elif hex_val.startswith('#'):
|
||||
hexstring = hex_val[1:]
|
||||
else:
|
||||
hexstring = hex_val
|
||||
else:
|
||||
return None
|
||||
if len(hexstring) % 2 != 0:
|
||||
|
|
@ -233,7 +238,7 @@ class Color(StructureBase):
|
|||
return None
|
||||
ba = []
|
||||
for i in xrange(0, len(hexstring), 2):
|
||||
ba.append(int(hexstring[i:i+2], 16))
|
||||
ba.append(int(hexstring[i:i+2], 16)/255.0)
|
||||
c = cls(*ba)
|
||||
return c
|
||||
|
||||
|
|
@ -249,6 +254,40 @@ class Color(StructureBase):
|
|||
return c
|
||||
return cls()
|
||||
|
||||
|
||||
class GradientStop(StructureBase):
|
||||
def __init__(self, offset=0, color=(1,1,1,1)):
|
||||
if offset > 1.0 or offset < 0:
|
||||
raise Exception('Offset must be between 0 and 1.')
|
||||
self.offset = offset
|
||||
self.color = Color.from_value(color)
|
||||
|
||||
|
||||
class Gradient(StructureBase):
|
||||
def __init__(self, start_position=(0,0), end_position=(0,1), stops=()):
|
||||
self._type = 'linear'
|
||||
self.stops = self.get_stops(stops)
|
||||
self.start_position = Position.from_value(start_position)
|
||||
self.end_position = Position.from_value(end_position)
|
||||
|
||||
def get_stops(self, stops):
|
||||
gstops = []
|
||||
for g_stop in stops:
|
||||
gstops.append(GradientStop.from_value(g_stop))
|
||||
return gstops
|
||||
|
||||
def add_stop(self, offset_pos, color):
|
||||
self.stops.append(GradientStop.from_value((offset_pos, color)))
|
||||
|
||||
|
||||
class RadialGradient(Gradient):
|
||||
def __init__(self, inner_radius=0, outer_radius=1, *args, **kwargs):
|
||||
super(RadialGradient, self).__init__(*args, **kwargs)
|
||||
self._type = 'radial'
|
||||
self.inner_radius = inner_radius
|
||||
self.outer_radius = outer_radius
|
||||
|
||||
|
||||
class Rectangle(StructureBase):
|
||||
def __init__(self, position=None, size=None):
|
||||
""" A rectangle object with coordinates and size.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import pango
|
|||
import pangocairo as pc
|
||||
import gtk
|
||||
import math
|
||||
from .structures import Size, Position, Rectangle, Color, BorderRadius, Padding
|
||||
from .structures import Size, Position, Rectangle, Color, BorderRadius, Padding, Gradient, RadialGradient
|
||||
from ..events.events import WindowEventSource
|
||||
from ..logger import log
|
||||
|
||||
|
|
@ -135,14 +135,17 @@ class WindowSurface(object):
|
|||
context.arc(0, 0, 1, start_angle, end_angle * math.pi/180.0)
|
||||
context.restore()
|
||||
|
||||
context.set_source_rgba(*color)
|
||||
context.set_source_rgba(color.r, color.g, color.b, color.a)
|
||||
context.fill_preserve()
|
||||
context.set_source_rgba(*line_color)
|
||||
context.set_source_rgba(line_color.r, line_color.g, line_color.b, line_color.a)
|
||||
context.stroke()
|
||||
|
||||
def draw_image(self, image, position, size,
|
||||
filter='none', stretch=True,
|
||||
keep_aspect=True, center_horizontal=True,
|
||||
filter='none',
|
||||
stretch_horizontal=False,
|
||||
stretch_vertical=False,
|
||||
keep_ratio=False,
|
||||
center_horizontal=True,
|
||||
center_vertical=True, image_offset=(0, 0)):
|
||||
|
||||
context = self.context
|
||||
|
|
@ -167,12 +170,18 @@ class WindowSurface(object):
|
|||
new_height = height
|
||||
new_width = width
|
||||
|
||||
if keep_aspect:
|
||||
aspect_ratio = min(im_width, im_height)/float(max(im_width, im_height))
|
||||
if keep_ratio:
|
||||
aspect_ratio = im_width/float(im_height)
|
||||
if width >= height:
|
||||
new_width = aspect_ratio * new_height
|
||||
if im_height < im_width:
|
||||
new_height = width/aspect_ratio
|
||||
else:
|
||||
new_width = aspect_ratio * height
|
||||
else:
|
||||
new_height = aspect_ratio * new_width
|
||||
if im_height > im_width:
|
||||
new_width = aspect_ratio * height
|
||||
else:
|
||||
new_height = width/aspect_ratio
|
||||
|
||||
if center_horizontal:
|
||||
x += width/2.0 - new_width/2.0
|
||||
|
|
@ -192,8 +201,15 @@ class WindowSurface(object):
|
|||
|
||||
ct3 = gtk.gdk.CairoContext(ct2)
|
||||
|
||||
if stretch:
|
||||
ct3.scale(new_width/float(im_width), new_height/float(im_height))
|
||||
new_scale_x = 1
|
||||
new_scale_y = 1
|
||||
|
||||
if stretch_horizontal or keep_ratio:
|
||||
new_scale_x = new_width/float(im_width)
|
||||
if stretch_vertical or keep_ratio:
|
||||
new_scale_y = new_height/float(im_height)
|
||||
|
||||
ct3.scale(new_scale_x, new_scale_y)
|
||||
|
||||
ct3.set_source_pixbuf(image, -offset.x, -offset.y)
|
||||
ct3.get_source().set_filter(self.filters[filter])
|
||||
|
|
@ -232,9 +248,7 @@ class WindowSurface(object):
|
|||
layout.set_alignment(alignment)
|
||||
|
||||
context.set_line_width(line_width)
|
||||
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)
|
||||
context.set_source_rgba(font_color.r, font_color.g, font_color.b, font_color.a)
|
||||
|
||||
extents = context.text_extents(text)
|
||||
|
||||
|
|
@ -244,7 +258,6 @@ class WindowSurface(object):
|
|||
context.move_to(x, y)
|
||||
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'):
|
||||
|
|
@ -267,18 +280,56 @@ class WindowSurface(object):
|
|||
context.set_line_join(self.line_caps[line_cap])
|
||||
except KeyError:
|
||||
pass
|
||||
context.set_source_rgba(*background_color)
|
||||
context.set_source_rgba(background_color.r, background_color.g, background_color.b, background_color.a)
|
||||
context.fill_preserve()
|
||||
context.set_source_rgba(*line_color)
|
||||
context.set_source_rgba(line_color.r, line_color.g, line_color.b, line_color.a)
|
||||
context.stroke()
|
||||
|
||||
def render_radial_gradient(self, gradient, inner_radius=None, outer_radius=None):
|
||||
context = self.context
|
||||
position = self.position
|
||||
size = self.size
|
||||
gradient = RadialGradient.from_value(gradient)
|
||||
rp = cairo.RadialGradient(gradient.start_position.x*float(size.width) + position.x,
|
||||
gradient.start_position.y*float(size.height) + position.y,
|
||||
inner_radius or gradient.inner_radius,
|
||||
gradient.end_position.x*float(size.width) + position.x,
|
||||
gradient.end_position.y*float(size.height) + position.y,
|
||||
outer_radius or gradient.outer_radius)
|
||||
for gstop in gradient.stops:
|
||||
rp.add_color_stop_rgba(gstop.offset, gstop.color.r, gstop.color.g,
|
||||
gstop.color.b, gstop.color.a)
|
||||
if gradient.stops:
|
||||
context.save()
|
||||
context.set_source(rp)
|
||||
context.fill_preserve()
|
||||
context.restore()
|
||||
|
||||
def draw_rounded_rect(self, position, size, background_color=(1,1,1), line_width=1, line_color=(0,0,0), corner_radius=0, line_dashed=False, clip=False):
|
||||
def render_linear_gradient(self, gradient):
|
||||
context = self.context
|
||||
position = self.position
|
||||
size = self.size
|
||||
gradient = Gradient.from_value(gradient)
|
||||
lp = cairo.LinearGradient(gradient.start_position.x*float(size.width) + position.x,
|
||||
gradient.start_position.y*float(size.height) + position.y,
|
||||
gradient.end_position.x*float(size.width) + position.x,
|
||||
gradient.end_position.y*float(size.height) + position.y)
|
||||
for gstop in gradient.stops:
|
||||
lp.add_color_stop_rgba(gstop.offset, gstop.color.r, gstop.color.g,
|
||||
gstop.color.b, gstop.color.a)
|
||||
if gradient.stops:
|
||||
context.save()
|
||||
context.set_source(lp)
|
||||
context.fill_preserve()
|
||||
context.restore()
|
||||
|
||||
def draw_rounded_rect(self, position, size, background_color=(1,1,1), line_width=1, line_color=(0,0,0), corner_radius=0, line_dashed=False, clip=False, gradient=()):
|
||||
position = Position.from_value(position)
|
||||
size = Size.from_value(size)
|
||||
background_color = Color.from_value(background_color)
|
||||
line_color = Color.from_value(line_color)
|
||||
corner_radius = BorderRadius.from_value(corner_radius)
|
||||
gradient = Gradient.from_value(gradient)
|
||||
|
||||
context = self.context
|
||||
radius = corner_radius
|
||||
|
|
@ -287,7 +338,9 @@ class WindowSurface(object):
|
|||
y = position.y + self.position.y
|
||||
width = size.width
|
||||
height = size.height
|
||||
|
||||
if clip: #clips the entire region so any child windows will be confined to the parent
|
||||
context.new_path()
|
||||
context.arc(x + width - radius.topright - line_width/2.0,
|
||||
y + radius.topright + line_width/2.0,
|
||||
radius.topright+self.border_width/2.0, -90 * degrees, 0 * degrees)
|
||||
|
|
@ -318,9 +371,18 @@ class WindowSurface(object):
|
|||
radius.topleft, 180 * degrees, 270 * degrees)
|
||||
context.close_path()
|
||||
|
||||
context.set_source_rgba(*background_color)
|
||||
if gradient.stops:
|
||||
if gradient._type == 'linear':
|
||||
self.render_linear_gradient(gradient)
|
||||
elif gradient._type == 'radial':
|
||||
self.render_radial_gradient(gradient)
|
||||
else:
|
||||
context.set_source_rgba(background_color.r, background_color.g, background_color.b, background_color.a)
|
||||
else:
|
||||
context.set_source_rgba(background_color.r, background_color.g, background_color.b, background_color.a)
|
||||
|
||||
context.fill_preserve()
|
||||
context.set_source_rgba(*line_color)
|
||||
context.set_source_rgba(line_color.r, line_color.g, line_color.b, line_color.a)
|
||||
context.set_line_width(line_width)
|
||||
context.save()
|
||||
if line_dashed:
|
||||
|
|
@ -329,6 +391,7 @@ class WindowSurface(object):
|
|||
context.restore()
|
||||
|
||||
if clip: #clips the entire region so any child windows will be confined to the parent
|
||||
context.new_path()
|
||||
context.arc(x + width - radius.topright - line_width/2.0,
|
||||
y + radius.topright + line_width/2.0,
|
||||
radius.topright-self.border_width/2.0, -90 * degrees, 0 * degrees)
|
||||
|
|
@ -380,12 +443,15 @@ class Window(WindowEventSource, WindowSurface):
|
|||
self.background_color = Color.from_value(kwargs.pop('background_color', (0,0,0,0)))
|
||||
self.background_image = self.load_image(kwargs.pop('background_image', None))
|
||||
self.background_image_filter = kwargs.pop('background_image_filter','none')
|
||||
self.background_image_stretch = kwargs.pop('background_image_stretch', True)
|
||||
self.background_image_keep_ratio = kwargs.pop('background_image_keep_ratio', True)
|
||||
self.background_image_stretch_horizontal = kwargs.pop('background_image_stretch_horizontal', False)
|
||||
self.background_image_stretch_vertical = kwargs.pop('background_image_stretch_vertical', False)
|
||||
self.background_image_keep_ratio = kwargs.pop('background_image_keep_ratio', False)
|
||||
self.background_image_center_horizontal = kwargs.pop('background_image_center_horizontal', True)
|
||||
self.background_image_center_vertical = kwargs.pop('background_image_center_vertical', True)
|
||||
self.background_image_offset = Position.from_value(kwargs.pop('background_image_offset', (0, 0)))
|
||||
|
||||
self.gradient = Gradient.from_value(kwargs.pop('gradient', ()))
|
||||
|
||||
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)
|
||||
|
|
@ -421,13 +487,14 @@ class Window(WindowEventSource, WindowSurface):
|
|||
line_width=self.border_width,
|
||||
corner_radius=self.border_radius,
|
||||
line_dashed=self.dashed_border,
|
||||
clip=self.clip_children)
|
||||
clip=self.clip_children, gradient=self.gradient)
|
||||
|
||||
if self.background_image is not None:
|
||||
self.draw_image(self.background_image, [0, 0], self.size,
|
||||
filter=self.background_image_filter,
|
||||
stretch=self.background_image_stretch,
|
||||
keep_aspect=self.background_image_keep_ratio,
|
||||
stretch_horizontal=self.background_image_stretch_horizontal,
|
||||
stretch_vertical=self.background_image_stretch_vertical,
|
||||
keep_ratio=self.background_image_keep_ratio,
|
||||
center_horizontal=self.background_image_center_horizontal,
|
||||
center_vertical=self.background_image_center_vertical,
|
||||
image_offset=self.background_image_offset)
|
||||
|
|
@ -463,25 +530,34 @@ 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):
|
||||
def _restrict_pos_size_height(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
|
||||
|
||||
return new_pos, new_size
|
||||
|
||||
def _restrict_pos_size_width(self, new_pos, new_size):
|
||||
|
||||
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.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 _restrict_pos_size(self, new_pos, new_size):
|
||||
self._restrict_pos_size_height(new_pos, new_size)
|
||||
self._restrict_pos_size_width(new_pos, new_size)
|
||||
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)
|
||||
|
|
@ -495,7 +571,7 @@ class Window(WindowEventSource, WindowSurface):
|
|||
new_pos = Position(self.position.x + diff.x, self.position.y)
|
||||
new_size = Size(self.size.width - diff.x, self.size.height + diff.y)
|
||||
|
||||
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
|
||||
new_pos, new_size = self._restrict_pos_size_width(new_pos, new_size)
|
||||
|
||||
if self.draggable:
|
||||
self.position = new_pos
|
||||
|
|
@ -512,7 +588,7 @@ class Window(WindowEventSource, WindowSurface):
|
|||
new_pos = Position(self.position.x, self.position.y + diff.y)
|
||||
new_size = Size(self.size.width + diff.x, self.size.height - diff.y)
|
||||
|
||||
new_pos, new_size = self._restrict_pos_size(new_pos, new_size)
|
||||
new_pos, new_size = self._restrict_pos_size_height(new_pos, new_size)
|
||||
|
||||
if self.draggable:
|
||||
self.position = new_pos
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from jgui.surface import Surface, Window, Position, Size, Color, TextWindow, ImageWindow
|
||||
from jgui.surface import Surface, Window, Position, Size, Color, TextWindow, ImageWindow, Gradient, RadialGradient
|
||||
import math, os
|
||||
from jgui.settings import IMG_DIR
|
||||
|
||||
|
|
@ -19,7 +19,13 @@ class TestSurface(Surface):
|
|||
clip_children=True,
|
||||
ignore_debug=True,
|
||||
background_image=os.path.join(IMG_DIR, 'wrench.png'),
|
||||
background_image_filter='bilinear')
|
||||
background_image_filter='bilinear',
|
||||
background_image_keep_ratio=True,
|
||||
background_image_center_vertical=True,
|
||||
background_image_center_horizontal=True,
|
||||
background_image_stretch_horizontal=True,
|
||||
background_image_stretch_vertical=True,
|
||||
gradient=RadialGradient(start_position=[0.5, 0], end_position=[0.5, 0.5], inner_radius=30, outer_radius=500, stops=[(0, Color(1,1,1)), (1, Color(0.3,0.3,0.3))]))
|
||||
|
||||
my_win.add_child(TextWindow('text','Micro Bean is the best bean ever.',
|
||||
position=[20,20], size=[100, 50],
|
||||
|
|
@ -28,6 +34,33 @@ class TestSurface(Surface):
|
|||
draggable=True))
|
||||
|
||||
self.root_window.add_child(my_win)
|
||||
|
||||
my_win2 = Window('linear_gradient',
|
||||
position=[400, 400],
|
||||
size=[500,500],
|
||||
draggable=True,
|
||||
resizable=True,
|
||||
min_size=Size(40,40),
|
||||
border_width=5,
|
||||
border_radius=[40,20],
|
||||
border_color=(0,0,0),
|
||||
background_color=(1,1,1),
|
||||
clip_children=True,
|
||||
ignore_debug=True,
|
||||
background_image=os.path.join(IMG_DIR, 'bluebanner.png'),
|
||||
background_image_filter='bilinear',
|
||||
background_image_keep_ratio=True,
|
||||
background_image_center_vertical=False,
|
||||
background_image_center_horizontal=False,
|
||||
gradient=Gradient(stops=[(0, Color(1,1,1)), (0.8, Color(0.5,0.5,0.5)), (1, Color(0.7,0.7,0.7))]))
|
||||
|
||||
my_win2.add_child(TextWindow('text2',"I haven't had a bean in forever.",
|
||||
position=[20,20], size=[200, 100],
|
||||
resizable=False, clip_children=True,
|
||||
font_color=(0,0,0), padding=20,
|
||||
draggable=False))
|
||||
|
||||
self.root_window.add_child(my_win2)
|
||||
#self.root_window.add_child(TestWindow('child2', position=[200,200],
|
||||
# size=[200,200], draggable=True))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue