################################################################################
# copyright 2010 Lucas Baudin <xapantu@gmail.com> #
# #
# This file is part of Ultimate Smash Friends. #
# #
# Ultimate Smash Friends 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 3 of the License, or (at your #
# option) any later version. #
# #
# Ultimate Smash Friends 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 #
# Ultimate Smash Friends. If not, see <http://www.gnu.org/licenses/>. #
################################################################################
'''
A nice coverflow effect for images
'''
#standard imports
import pygame
import os
#our module
from usf.widgets.widget import Widget
from usf.font import fonts
from usf import loaders
from usf import CONFIG
[docs]def get_text_transparent(name):
text = loaders.text(name, fonts['mono']['10']).convert()
#FIXME: the colorkey should be in a skin configuration file
text.fill(pygame.color.Color("black"))
text.set_colorkey(pygame.color.Color("black"))
text.blit(loaders.text(name, fonts['mono']['22']), (0, 0))
return text
[docs]class Coverflow(Widget):
"""
The coverflow widget is used to display many image and choose one of
the them.
It should be big, about 800 * 275. This widget is animated and requires
a lot of CPU.
"""
#the animation() function wil be called each frame
#FIXME: the animation speed souldn't depend of the computer
animation_speed = True
def __init__(self, values):
super(Coverflow, self).__init__()
self.values = values
for value in self.values:
#adding (false) size for the image, there will be updated later
value.append((None, None))
value.append(None)
self.in_anim = False
self.anim_state = ""
self.advance = 0
#compatibility with the others widget only
self.state = False
self.set_size((800, 230)) # FIXME hardcoded value
self.center_size = (self.sizex(195), self.sizey(120))
self.posy_center = self.sizey(60)
self.foreground = loaders.image(
os.path.join(CONFIG.system_path,
"gui",
CONFIG.general.THEME,
"coverflow",
"foreground.png"),
scale=(CONFIG.general.WIDTH, CONFIG.general.HEIGHT))[0]
self.frame = loaders.image(
os.path.join(CONFIG.system_path,
"gui",
CONFIG.general.THEME,
"coverflow",
"frame.png"),
scale=(self.sizex(137), self.sizey(86)))
self.surface = pygame.surface.Surface((self.width, self.height))
self.index = 0
self.text = get_text_transparent(self.values[self.index][0])
self.previous()
self.load_main_frame()
for value in self.values:
size = loaders.image(value[1])[1]
if size[3] > size[2]:
value[2] = (
self.frame[1][2] - self.sizex(25),
size[3] * (
self.frame[1][2] - self.sizex(25)) / size[2])
else:
value[2] = (
size[2] * (
self.frame[1][3] - self.sizex(25)) / size[3],
self.frame[1][3] - self.sizey(25))
value[3] = (self.frame[1][2] / 2 - value[2][0]/2,
self.frame[1][3] / 2 - value[2][1]/2)
self.need_update = True
self.screen = pygame.display.get_surface()
[docs] def draw(self):
"""
Draw the widget, the surface will be redrawed if the widget is animated.
You can force redrawing by set need_update to True.
"""
x, y = (self.parentpos[0] + self.x, self.parentpos[1] + self.y)
self.pos = self.width/2 - self.main_frame.get_width()/2 + self.advance
self.draw_main()
self.draw_right()
self.draw_left()
reflection = pygame.transform.flip(self.surface, False, True)
reflection.set_colorkey(pygame.color.Color("black"))
#reflection.set_alpha(20)
self.screen.blit(reflection, (0, self.sizey(100)))
self.screen.blit(self.text, (
x + self.width/2 - self.text.get_width()/4,
y + self.sizey(30)))
self.screen.blit(self.foreground, (0, 0))
self.need_update = False
if self.in_anim:
self.start_anim()
[docs] def draw_main(self):
"""
Draw the selected image, it is bigger than the other and in the center
"""
x, y = (self.parentpos[0] + self.x, self.parentpos[1] + self.y)
#main frame
self.screen.blit(self.main_frame, (x + self.pos, y + self.posy_center))
self.screen.blit(loaders.image(
self.values[self.index][1],
scale=self.center_image)[0], (
x + self.pos + self.center_image_indent[0],
y + self.posy_center + self.center_image_indent[1]))
self.pos += self.main_frame.get_width()
[docs] def draw_right(self):
"""
Draw three small image at right.
"""
x, y = (self.parentpos[0] + self.x, self.parentpos[1] + self.y)
for i in range(
self.index - len(self.values) + 1,
self.index - len(self.values) + 4):
self.screen.blit(self.frame[0], (x + self.pos, y + self.sizey(82)))
self.screen.blit(loaders.image(
self.values[i][1],
scale=self.values[i][2])[0], (
x + self.pos + self.values[i][3][0],
y + self.sizey(82) + self.values[i][3][1]))
self.pos += self.frame[1][2]
[docs] def draw_left(self):
"""
Draw three small image at left.
"""
x, y = (self.parentpos[0] + self.x, self.parentpos[1] + self.y)
#at left now
self.pos = (
self.width/2
- self.main_frame.get_width()/2
- self.frame[1][2] * 3
+ self.advance)
for i in range(self.index - 3, self.index):
self.screen.blit(self.frame[0], (x + self.pos, y + self.sizey(82)))
self.screen.blit(loaders.image(
self.values[i][1],
scale=self.values[i][2])[0], (
x + self.pos + self.values[i][3][0],
y + self.sizey(82) + self.values[i][3][1]))
self.pos += self.frame[1][2]
[docs] def next(self):
"""
Select the next item.
"""
self.index = (self.index - 1) % len(self.values)
self.text = get_text_transparent(self.values[self.index][0])
[docs] def previous(self):
"""
Select the previous item.
"""
self.index = (self.index + 1) % len(self.values)
self.text = get_text_transparent(self.values[self.index][0])
[docs] def handle_mouse(self, event):
"""
This function handle all mouse events which are on the widget.
"""
if not self.in_anim:
if event.type == pygame.MOUSEBUTTONDOWN:
x = event.dict['pos'][0]
# y = event.dict['pos'][1] #
if x > self.width/2 + self.frame[1][2] / 2:
self.launch_anim(False)
elif x < self.width/2 - self.frame[1][2] / 2:
self.launch_anim(True)
return False, False
[docs] def launch_anim(self, direction):
""" start a translation animation in the direction
"""
#self.last_c_update = time.time()
self.anim_state = "start"
self.last_index = self.index
self.in_anim = True
self.sens = direction
[docs] def animation(self):
""" update the animation
"""
if self.in_anim:
if self.anim_state == "start" or self.anim_state == "slide":
if self.center_size[0] - self.sizex(10) > self.sizey(137):
w = self.center_size[0] - self.sizex(30)
h = self.center_size[1] * w / self.center_size[0]
#self.advance += self.sizex(5)
self.text.set_alpha(self.text.get_alpha() - 50)
else:
self.anim_state = "slide"
self.text.set_alpha(0)
w = self.sizey(137)
h = self.sizey(86)
self.posy_center = self.sizey(125) - h/2
self.center_size = (w, h)
self.load_main_frame()
if self.sens:
if self.advance + 40 < self.frame[1][2]:
self.advance += 40
else:
self.advance = self.frame[1][2]
self.anim_state = "change"
else:
if self.advance - 40 > - (self.frame[1][2]):
self.advance -= 40
else:
self.advance = - self.frame[1][2]
self.anim_state = "change"
elif self.anim_state == "change":
if self.sens:
self.next()
else:
self.previous()
self.text.set_alpha(0)
self.advance = 0
self.anim_state = "end"
elif self.anim_state == "end":
if self.center_size[0] < self.sizey(195):
w = self.center_size[0] + self.sizex(30)
h = self.center_size[1] * w / self.center_size[0]
#self.advance -= self.sizex(5)
self.text.set_alpha(self.text.get_alpha() + 50)
else:
w = self.sizex(195)
h = self.sizey(120)
self.text.set_alpha(250)
#self.in_anim = False
self.anim_state = ""
self.advance = 0
self.posy_center = self.sizey(125) - h/2
self.center_size = (w, h)
self.load_main_frame()
self.need_update = True
elif self.anim_state == "":
self.in_anim = False
self.need_update = True
[docs] def handle_keys(self, event):
""" manage keyboard input events
"""
if (
event.dict["key"] == pygame.K_DOWN or
event.dict["key"] == pygame.K_UP) and not self.state:
self.state = True
return False, self
if event.dict["key"] == pygame.K_RETURN:
return self, self
if not self.in_anim and (event.dict["key"] == pygame.K_LEFT
or event.dict["key"] == pygame.K_RIGHT):
if event.dict["key"] == pygame.K_LEFT:
self.launch_anim(True)
if event.dict["key"] == pygame.K_RIGHT:
self.launch_anim(False)
return self, self
self.state = False
return False, False
[docs] def load_main_frame(self):
""" #FIXME get xapantu to document a little! :P
"""
self.main_frame = loaders.image(os.path.join(CONFIG.system_path,
"gui",
CONFIG.general.THEME,
"coverflow",
"frame.png"),
scale=self.center_size)[0]
img = loaders.image(self.values[self.index][1])[0]
#keep the image ratio
if img.get_height() > img.get_width():
self.center_image = (
self.main_frame.get_width() - self.sizex(25),
img.get_height() * (self.main_frame.get_width() -
self.sizex(25)) / img.get_width())
else:
self.center_image = (
img.get_width() * (
self.main_frame.get_height() - self.sizey(25))
/ img.get_height(),
self.main_frame.get_height()- self.sizey(25))
self.center_image_indent = (
self.main_frame.get_width()/2 - self.center_image[0]/2,
self.main_frame.get_height()/2 - self.center_image[1]/2)
[docs] def get_value(self):
""" return the currently selected value
"""
return self.values[self.index][0]
[docs] def sizex(self, x):
"""
This method is used to get the real size of an element. All size in the
code above are for a widget size = 800*275. So, if the size is
different, this function modify it. This function is used for the x
axis. Use sizey for y axis.
"""
return x * self.width / 800
[docs] def sizey(self, y):
"""
Same as sizex, but for the y axis.
"""
return y * self.height / 275