Merge lp:~samuel-buffet/entertainer/reactive_menus into lp:entertainer

Proposed by Samuel Buffet
Status: Merged
Approved by: Matt Layman
Approved revision: 513
Merged at revision: not available
Proposed branch: lp:~samuel-buffet/entertainer/reactive_menus
Merge into: lp:entertainer
Diff against target: None lines
To merge this branch: bzr merge lp:~samuel-buffet/entertainer/reactive_menus
Reviewer Review Type Date Requested Status
Matt Layman Approve
Review via email: mp+8745@code.launchpad.net

Commit message

All menus are now reactive to mouse pointer events.

To post a comment you must log in.
Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Devs,

Finally the day has come.

I think it's now time for me to propose this work around menus.

The goal was first to make menu pointer reactive, but it turned out that I've done a GridMenu refactoring at the same time. For me it was just none sense to change the current GridMenu.

As menus are everywhere, the branch is quite big (the diff is around 5kline).
I know that this branch is far from being perfect, but if I wait longer then it will become very hard to land that work.

As it is not perfect, I'm prepared for a long and hard review with probably a lot of discussions.

But definitely, the work is enough mature to be proposed. A lot of other things can be done but it can be done later imho.

Good review.

Samuel-

Revision history for this message
Matt Layman (mblayman) wrote :

Cripes!! This is going to take quite a long time to review. :)

Do you have suggestions of where to start? What's the core change? Some widget? GridMenu? I typically like to look at the core change, then examine everything that it affected. That might just be the entire frontend in this case, but I thought I'd ask in case there was hope for me. ;)

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

> Cripes!! This is going to take quite a long time to review. :)
>
> Do you have suggestions of where to start? What's the core change? Some
> widget? GridMenu? I typically like to look at the core change, then examine
> everything that it affected. That might just be the entire frontend in this
> case, but I thought I'd ask in case there was hope for me. ;)

Well, everything starts from the GridMenu which has been greatly refactored.
Then, the classes inheriting from it: ImageMenu, TextMenu.
Then maybe the relative menuitems.
And to finish the screens.

That might be a good order.

The GridMenu looks difficult but is quite simple in fact. There's only a couple of methods that will need a bit of abstraction (those linked to the cursor's motion).

Keep in mind that the GridMenu is a group containing a moving group which is translated on x,y and a cursor also translatable on x,y.

The rest of the code is really classic and easy to read.

Samuel-

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

I forgot to mention that there's also a new specif behaviour.

We now have 2, LoopedPathBehaviour and the new FontSizeBehaviour used to do a font size effect.

When clutter 1.0 will be released, we'll have quite a lot of work. Not only to change our code to the new API but also to see if there's no better way to do things with it.

Samuel-

Revision history for this message
Matt Layman (mblayman) wrote :
Download full text (3.7 KiB)

Well, here we go. Please keep in mind that these comments are basically everything that I could think of. I understand if not all comments are landed in this branch. This is just comments for the two files listed below (because it's all I'm going to review tonight). More comments will come later, but this is at least something to chew on.

base.py:
 * y_for_x docstring isn't very clear (gramatically). The word "than" just doesn't work well in that context. I think you could replace "than" with "equivalent to".
 * Could you add a test for this method?

grid_menu.py:
 * Is this widget testable? I've got to believe that somewhere in those 561 lines is something that can be tested. :)
 * import order. math is a Python standard library module.
 * Please add something meaningful to the class docstring.
 * I'm fairly lost by the init. There are so many instance variables. You seem to have some groupings, but could you add some comments to help us understand what those groupings are?
 * init seems to have some default width and heights. The values seem like they are pixels and not percentages. Why is that? Could they be percentages instead?
 * self.motion_duration in init: what is the unit of time for it? 100 is just a magic number right now.
 * _item_width_abs for the default value of item_width=200 will be enormous. Way larger than the stage.
 * event rect color of Orange. I like it. :D
 * When the branch lands, please file a bug about your XXX in the init. I'm okay with that XXX comment landing.
 * on_bottom & on_right: you could move the end_row calculation into the if statement of _is_vertical to slightly improve performance of horizontal menus. (Remember, these comments are *everything* I could think of :D).
 * _set_selected_index: "relatively" should be "relative"
 * _set_selected_index comment needs cleanup (start with "#start select...").
 * What is the cursor in _set_selected_index comments? Is that the mouse cursor? Is it some other cursor that is tracked by the GridMenu? I'm having a hard time figuring it out. Maybe there could be a comment by cursor property definition.
 * I got to the direction methods (up, down, left, right) and said to myself "WTF!? Why isn't there code to move something?" I then realized that the _set_selected_index method triggers the actual movement. I think it would be wise to mention this in the docstring.
 * Xc and Yc seems like the coordinates of "index" to me (line 234) and not like "coordinates of the cursor relatively to the moving group". Am I missing something?
 * Could you add some comments to the series of if statements in _set_selected_index? Mapping variable names in my head (because xm, Xc, and xc aren't very descriptive) and determining boolean logic at the same time is difficult to do. I'm sure the rules make sense, but it's hard as a reader to immediately understand what the code is trying to accomplish in those lines.
 * What is items_anchor_west? Everywhere that it is used in the code, it is set to False. And only one part of GridMenu uses it (raw_add_item). Therefore, the if portion of "if self.items_anchor_west" will never execute.
 * Could you expand the raw_add_item docstring? For example, wh...

Read more...

review: Needs Fixing
508. By Samuel Buffet

Fixes after first set of comments from Matt (part 1).

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Evening Matt,

See my first set of fixes.

The fixes are for this part of your comments.
To be continued.

Samuel-

> base.py:
> * y_for_x docstring isn't very clear (gramatically). The word "than" just
> doesn't work well in that context. I think you could replace "than" with
> "equivalent to".
> * Could you add a test for this method?
>
> grid_menu.py:
> * Is this widget testable? I've got to believe that somewhere in those 561
> lines is something that can be tested. :)
> * import order. math is a Python standard library module.
> * Please add something meaningful to the class docstring.
> * I'm fairly lost by the init. There are so many instance variables. You seem
> to have some groupings, but could you add some comments to help us understand
> what those groupings are?
> * init seems to have some default width and heights. The values seem like
> they are pixels and not percentages. Why is that? Could they be percentages
> instead?
> * self.motion_duration in init: what is the unit of time for it? 100 is just
> a magic number right now.
> * _item_width_abs for the default value of item_width=200 will be enormous.
> Way larger than the stage.
> * event rect color of Orange. I like it. :D
> * When the branch lands, please file a bug about your XXX in the init. I'm
> okay with that XXX comment landing.
> * on_bottom & on_right: you could move the end_row calculation into the if
> statement of _is_vertical to slightly improve performance of horizontal menus.
> (Remember, these comments are *everything* I could think of :D).
> * _set_selected_index: "relatively" should be "relative"
> * _set_selected_index comment needs cleanup (start with "#start select...").
> * What is the cursor in _set_selected_index comments? Is that the mouse
> cursor? Is it some other cursor that is tracked by the GridMenu? I'm having a
> hard time figuring it out. Maybe there could be a comment by cursor property
> definition.
> * I got to the direction methods (up, down, left, right) and said to myself
> "WTF!? Why isn't there code to move something?" I then realized that the
> _set_selected_index method triggers the actual movement. I think it would be
> wise to mention this in the docstring.
> * Xc and Yc seems like the coordinates of "index" to me (line 234) and not
> like "coordinates of the cursor relatively to the moving group". Am I missing
> something?
> * Could you add some comments to the series of if statements in
> _set_selected_index? Mapping variable names in my head (because xm, Xc, and xc
> aren't very descriptive) and determining boolean logic at the same time is
> difficult to do. I'm sure the rules make sense, but it's hard as a reader to
> immediately understand what the code is trying to accomplish in those lines.

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :
Download full text (17.4 KiB)

incremental diff:

=== modified file 'entertainerlib/frontend/gui/widgets/base.py'
--- entertainerlib/frontend/gui/widgets/base.py 2009-05-24 14:28:19 +0000
+++ entertainerlib/frontend/gui/widgets/base.py 2009-07-15 21:22:17 +0000
@@ -24,7 +24,7 @@
     def y_for_x(self, percentage):
         """
         Returns the percentage of stage height giving the same number of pixels
- than `percentage` applied to the stage width.
+ equivalent to `percentage` applied to the stage width.
         """
         stage_ratio = float(self.config.get_stage_width())
         stage_ratio /= self.config.get_stage_height()

=== modified file 'entertainerlib/frontend/gui/widgets/grid_menu.py'
--- entertainerlib/frontend/gui/widgets/grid_menu.py 2009-05-28 19:12:05 +0000
+++ entertainerlib/frontend/gui/widgets/grid_menu.py 2009-07-15 21:22:17 +0000
@@ -1,16 +1,20 @@
 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
 '''GridMenu contains a grid of MenuItem based widgets.'''

+import math
+
 import clutter
 import gobject
-import math

 from entertainerlib.frontend.gui.widgets.base import Base
 from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer

 class GridMenu(Base, clutter.Group):
     """
- GridMenu widget
+ GridMenu widget.
+
+ A core widget to handle MenuItem in a grid with a cursor.
+ This widget provides all the necessary logic to move items and the cursor.
     """
     __gsignals__ = {
         'activated' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
@@ -22,17 +26,18 @@
     MODE_SELECT = 1
     MODE_SEEK = 2

- def __init__(self, x=0, y=0, item_width=200, item_height=100):
+ def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1):
         Base.__init__(self)
         clutter.Group.__init__(self)

- self.motion_duration = 100
- self.cursor_below = True
+ self.motion_duration = 100 # Default duration for animations.
+ self.cursor_below = True # Is the cursor below items?
         self._active = None
         self._is_vertical = True
- self.items_anchor_west = False
+ self.items_anchor_west = False # Special anchor mode for items.
         self.items = []

+ # Items dimensions variable: relative, absolute, center
         self._item_width = item_width
         self._item_height = item_height
         self._item_width_abs = self.get_abs_x(item_width)
@@ -40,23 +45,30 @@
         self._dx = int(self._item_width_abs / 2)
         self._dy = int(self._item_height_abs / 2)

+ # Default cursor's index.
         self._selected_index = 0
+
+ # Grid dimensions: real, visible
         self.items_per_row = 10
         self.items_per_col = 10
         self._visible_rows = 3
         self._visible_cols = 5

+ # The moving_group is a Clutter group containing all the items.
         self._moving_group_x = 0
         self._moving_group_y = 0
         self._moving_group = clutter.Group()
         self.add(self._moving_group)

+ # The moving_group is translated using a `BehaviourPath`.
         self._moving_group_timeline = clutter.Timeline(fps=200, duration=200)
         moving_...

509. By Samuel Buffet

Fixed a stupid regression.

Revision history for this message
Matt Layman (mblayman) wrote :

Review comment of the day:

image_menu.py:
 * I need to raise a point that will be contentious. I have to disagree with putting specific logic for videos/albums/clips embedded into a widget. IMO, the only async_add method should be async_add, not async_add_*. I know that this puts more burden on the screen objects, but I strongly believe that classes should serve only the purpose that they were intended for. In the case of the ImageMenu, I think it should only know how to display images that are provided to it. I think that the easy solution would be to move the asyn specific method back into the screen/tab that it came from.

I have a decent idea for how to keep the async_add functionality in ImageMenu, but keep the specific logic out. We can talk about it on IRC. It involves concrete factories as input parameters to async_add (if that makes any sense to you right now, then our conversation on IRC will be a short one).

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :

Hi Matt,

> image_menu.py:
> * I need to raise a point that will be contentious. I have to disagree with
> putting specific logic for videos/albums/clips embedded into a widget. IMO,
> the only async_add method should be async_add, not async_add_*. I know that
> this puts more burden on the screen objects, but I strongly believe that
> classes should serve only the purpose that they were intended for. In the case
> of the ImageMenu, I think it should only know how to display images that are
> provided to it. I think that the easy solution would be to move the asyn
> specific method back into the screen/tab that it came from.

I have to agree with you, this was also my concern doing that. I was pretty happy to be able to add items asynchronously but definitely not with adding specific methods for specific items.

Now questions are, what to do instead, where (I'd prefer to do that in another branch if it's okay for you).

> I have a decent idea for how to keep the async_add functionality in ImageMenu,
> but keep the specific logic out. We can talk about it on IRC. It involves
> concrete factories as input parameters to async_add (if that makes any sense
> to you right now, then our conversation on IRC will be a short one).

Okay, let's talk of tha on IRC.

Samuel-

(missing tests for text and image menus are on the road.)

510. By Samuel Buffet

Fixes after first set of comments from Matt (part 2).

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :
Download full text (4.9 KiB)

Matt,

I've pushed my second part of fixes.

> base.py:
> * y_for_x docstring isn't very clear (gramatically). The word "than" just
> doesn't work well in that context. I think you could replace "than" with
> "equivalent to".
> * Could you add a test for this method?

Fixed.

> grid_menu.py:
> * Is this widget testable? I've got to believe that somewhere in those 561
> lines is something that can be tested. :)

Yeah of course. Added. (other menus will come).

> * import order. math is a Python standard library module.
> * Please add something meaningful to the class docstring.

Fixed.

> * I'm fairly lost by the init. There are so many instance variables. You seem
> to have some groupings, but could you add some comments to help us understand
> what those groupings are?

I've added more comments. It should be better.

> * init seems to have some default width and heights. The values seem like
> they are pixels and not percentages. Why is that? Could they be percentages
> instead?

Yeah it was a mistake. That's fixed.

> * self.motion_duration in init: what is the unit of time for it? 100 is just
> a magic number right now.

Here it's a regular timeline duration in ms.

> * _item_width_abs for the default value of item_width=200 will be enormous.
> Way larger than the stage.
> * event rect color of Orange. I like it. :D

:D Saddly it was a debug fossil so I've removed that line.

> * When the branch lands, please file a bug about your XXX in the init. I'm
> okay with that XXX comment landing.

New that. That was my intention.

> * on_bottom & on_right: you could move the end_row calculation into the if
> statement of _is_vertical to slightly improve performance of horizontal menus.
> (Remember, these comments are *everything* I could think of :D).

Changed.

> * _set_selected_index: "relatively" should be "relative"

Also fixed.

> * _set_selected_index comment needs cleanup (start with "#start select...").

Fixed.

> * What is the cursor in _set_selected_index comments? Is that the mouse
> cursor? Is it some other cursor that is tracked by the GridMenu? I'm having a
> hard time figuring it out. Maybe there could be a comment by cursor property
> definition.

I've change the docstring to specify that we're dealing with the menu's cursor.
Cursor's coordinates are linked to the cursor's index (index in the item list) for a specific menu geometry. So if we have x, y = 0, 0 (which is equivalent to index = 0) the the cursor is on top/left "Cell" of the menu.

> * I got to the direction methods (up, down, left, right) and said to myself
> "WTF!? Why isn't there code to move something?" I then realized that the
> _set_selected_index method triggers the actual movement. I think it would be
> wise to mention this in the docstring.

Done.

> * Xc and Yc seems like the coordinates of "index" to me (line 234) and not
> like "coordinates of the cursor relatively to the moving group". Am I missing
> something?
> * Could you add some comments to the series of if statements in
> _set_selected_index? Mapping variable names in my head (because xm, Xc, and xc
> aren't very descriptive) and determining boolean logic at the same tim...

Read more...

Revision history for this message
Matt Layman (mblayman) wrote :

Samuel,

You don't need to bother with adding new diffs for me. I either use LP loggerhead to look at the commit or I just look at the branch when I'm merging the code and testing it.

I'll hopefully get some more time to look at it when I get home from work today (more than 8 hours from now).

Revision history for this message
Matt Layman (mblayman) wrote :
Download full text (6.0 KiB)

Round 2
=======

grid_menu.py:
 * I counted 32 instance variables in use declared by the init. I know that GridMenu does a ton of stuff and I have no expectations for major refactorings of this branch, but maybe there are some class abstractions that can be extracted out somewhere else (because there's a lot of stuff to think about in this class). Maybe you have some thoughts on this subject. What do you think? Is there future common classes that could be pulled out from GridMenu?
 * Line 80 comment "usefull" should be "useful" (English is silly, I know. :)
 * _set_selected_index comment needs cleanup (start with "#start select..."). Still not fixed.
 * I think I may have push you too far with the comments for the series of if statements in _set_selected_index. Rather than a textual description of what the logic is saying for each if statement, I think what I meant to ask for is "What are the if statements doing as a whole?" Once I have an idea of what the if statements are supposed to accomplish, I should have a good idea of how to read the code. In all of these if statements, it looks like you're adjusting xm, ym, xc, or yc. Maybe these if statements could be extracted into a method to help explain what the section is supposed to do. As a good rule of thumb that I use personally, if you have to write more lines of comments than code, something probably needs to be changed.
 * I caught one more items_anchor_west still in the init.

image_menu.py:
 * You've already seen my comment about the specific types of async_add. I'll let it land, but please add an XXX comment so that we remember to come back to it.
 * async_add items are a pack of information, but there is no comment explaining what an item is (texture and data). I think that this could use a clarifying comment.

image_menu_item.py:
 * Glad you condensed this class into the image menu file.

label.py:
 * Can you add a color property test?

menu.py:
 * One more unneeded widget down!

menu_item.py:
 * No comment.

scroll_menu.py:
 * No comment.

selector.py:
 * No comment.

special_behaviours.py:
 * No comment.

tab_group.py:
 * No comment.

text_menu.py:
 * "self.extra_label.position = ( \" Is the continuation character helpful here?
 * ArtistsMenuItem. I don't think this needs to be specific to artists (even though it may be the only place it is used right now). How about AnimatingMenuItem instead?
 * self.timeline is only used by ArtistsMenuItem, not the parent TextMenuItem. Could you please move it to the subclass?
 * add_item also calls a mysterious pack item. A comment might help.
 * TextMenu also has the context specific async_add_artists method. I'll let this land too, but please add an XXX comment here too.

text_menu_item.py:
 * 1 less file, nice.

music.py:
 * I did this by mistake too in a different branch that I was working on (and should hopefully finish some day), but get_length_string should not zfill the first value. It should just zfill the second (otherwise it means there is a leading zero on the minute when we don't need there to be one). The places that in-lined this code only used one zfill as expected.

mock.py:
 * No comment.

test_base.py:
 * No comment.

test_gr...

Read more...

review: Approve
511. By Samuel Buffet

Fixed another docstring in the GridMenu.

512. By Samuel Buffet

Fixes after Matt's comment round 2.

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :
Download full text (6.9 KiB)

Evening Matt,

> grid_menu.py:
> * I counted 32 instance variables in use declared by the init. I know that
> GridMenu does a ton of stuff and I have no expectations for major refactorings
> of this branch, but maybe there are some class abstractions that can be
> extracted out somewhere else (because there's a lot of stuff to think about in
> this class). Maybe you have some thoughts on this subject. What do you think?
> Is there future common classes that could be pulled out from GridMenu?

Short answer Yes.
Indeed this is something I have in mind since a long time. I'm deeply convinced that there's a lot of line of code we can save and a much better readability we can reach doing a widget API refactoring which would take better usage of inheritance.
>>> http://wiki.entertainer-project.com/wiki/BluePrints/widget-API-changes
This is still in my mind (a few updates needed though).

> * Line 80 comment "usefull" should be "useful" (English is silly, I know. :)

Fixed.

> * _set_selected_index comment needs cleanup (start with "#start select...").
> Still not fixed.

Was fixed in a standing alone commit.

> * I think I may have push you too far with the comments for the series of if
> statements in _set_selected_index. Rather than a textual description of what
> the logic is saying for each if statement, I think what I meant to ask for is
> "What are the if statements doing as a whole?" Once I have an idea of what the
> if statements are supposed to accomplish, I should have a good idea of how to
> read the code. In all of these if statements, it looks like you're adjusting
> xm, ym, xc, or yc. Maybe these if statements could be extracted into a method
> to help explain what the section is supposed to do. As a good rule of thumb
> that I use personally, if you have to write more lines of comments than code,
> something probably needs to be changed.

Agreed, so what I propose to you is that I'll create a wiki page with drawings and text to better explain that. And when it will be totally clear for you then maybe you'll be able to help be to find better words. I'll try to do that this week.

> * I caught one more items_anchor_west still in the init.

Yep, fixed now.

> image_menu.py:
> * You've already seen my comment about the specific types of async_add. I'll
> let it land, but please add an XXX comment so that we remember to come back to
> it.

Done.

> * async_add items are a pack of information, but there is no comment
> explaining what an item is (texture and data). I think that this could use a
> clarifying comment.

Done.

> label.py:
> * Can you add a color property test?

Indeed that was missing. I've added a test for this property.

> text_menu.py:
> * "self.extra_label.position = ( \" Is the continuation character helpful
> here?

Absolutely not. That's fixed.

> * ArtistsMenuItem. I don't think this needs to be specific to artists (even
> though it may be the only place it is used right now). How about
> AnimatingMenuItem instead?

Yes you're right. I've renamed this Class.

> * self.timeline is only used by ArtistsMenuItem, not the parent TextMenuItem.
> Could you please move it to the subclass?

Agreed with ...

Read more...

Revision history for this message
Samuel Buffet (samuel-buffet) wrote :
Download full text (17.9 KiB)

incremental diff:

=== modified file 'entertainerlib/frontend/gui/screens/disc.py'
--- entertainerlib/frontend/gui/screens/disc.py 2009-07-12 07:57:05 +0000
+++ entertainerlib/frontend/gui/screens/disc.py 2009-07-19 19:27:31 +0000
@@ -71,7 +71,13 @@
             self.track_menu = self._create_track_menu(tracks)
             self.add(self.track_menu)
             self._create_album_cover_texture(artist, title)
- self._create_list_indicator()
+
+ self.li = ListIndicator(0.75, 0.8, 0.2, 0.045,
+ ListIndicator.VERTICAL)
+ tracks = \
+ self.music_library.get_compact_disc_information().get_tracks()
+ self.li.set_maximum(len(tracks))
+ self.add(self.li)

             art_file = os.path.join(self.config.ALBUM_ART_DIR,
                 artist + " - " + title + ".jpg")
@@ -185,13 +191,6 @@

         return menu

- def _create_list_indicator(self):
- '''Create list indicator for track list.'''
- self.li = ListIndicator(0.75, 0.8, 0.2, 0.045, ListIndicator.VERTICAL)
- tracks = self.music_library.get_compact_disc_information().get_tracks()
- self.li.set_maximum(len(tracks))
- self.add(self.li)
-
     def _handle_up(self):
         '''Handle UserEvent.NAVIGATE_UP.'''
         self.track_menu.up()

=== modified file 'entertainerlib/frontend/gui/screens/main.py'
--- entertainerlib/frontend/gui/screens/main.py 2009-06-29 05:25:01 +0000
+++ entertainerlib/frontend/gui/screens/main.py 2009-07-19 19:27:31 +0000
@@ -47,6 +47,8 @@
         self.menu = self._create_main_menu()
         self.add(self.menu)

+ self._update_preview_area()
+
         self.add(ClockLabel(0.13, "screentitle", 0, 0.87))

         self.menu.connect('selected', self._handle_select)
@@ -60,11 +62,11 @@
         return Screen.NORMAL

     def get_name(self):
- """Return screen name (human readble)"""
+ """Return screen name (human readble)."""
         return "Main"

     def _create_main_menu(self):
- """Create main menu of the home screen"""
+ """Create main menu of the home screen."""
         menu = ScrollMenu(10, 60, 0.045, "menuitem_active")
         menu.set_name("mainmenu")

=== modified file 'entertainerlib/frontend/gui/screens/movie.py'
--- entertainerlib/frontend/gui/screens/movie.py 2009-07-12 07:57:05 +0000
+++ entertainerlib/frontend/gui/screens/movie.py 2009-07-19 19:27:31 +0000
@@ -155,7 +155,7 @@
         """
         Create menu of the screen. This displayed the left side of the screen.
         """
- self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781)
+ self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781)
         self.menu.add_item(_("Watch"), None, "watch")
         self.menu.active = True

=== modified file 'entertainerlib/frontend/gui/screens/photo_albums.py'
--- entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-12 07:57:05 +0000
+++ entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-19 19:27:31 +0000
@@ -240,7 +240,10 @@
     def _create_list_indicator(self):
         '''Create list indicator for albums list.'''
         self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndic...

513. By Samuel Buffet

Removed a redundant "tracks" variable in disc.py.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'entertainerlib/frontend/gui/screens/album.py'
--- entertainerlib/frontend/gui/screens/album.py 2009-05-10 07:43:27 +0000
+++ entertainerlib/frontend/gui/screens/album.py 2009-07-14 10:46:47 +0000
@@ -11,7 +11,6 @@
11from entertainerlib.frontend.gui.widgets.label import Label11from entertainerlib.frontend.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
14from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1514
16class Album(Screen):15class Album(Screen):
17 '''Screen that allows user to browse and play tracks of the music album.'''16 '''Screen that allows user to browse and play tracks of the music album.'''
@@ -28,21 +27,25 @@
28 self.track_menu = None27 self.track_menu = None
2928
30 # Create and initialize screen items29 # Create and initialize screen items
31 self._create_track_menu()30 self.track_menu = self._create_track_menu()
31 self.add(self.track_menu)
32 self._create_album_cover_texture()32 self._create_album_cover_texture()
33 self._create_album_info()33 self._create_album_info()
3434
35 self.screen_title = Label(0.13, "screentitle", 0, 0.87, "")35 self.screen_title = Label(0.13, "screentitle", 0, 0.87, "")
36 self.screen_title.set_ellipsize(pango.ELLIPSIZE_END)36 self.screen_title.set_ellipsize(pango.ELLIPSIZE_END)
37 self.screen_title.width = 0.837 self.screen_title.width = 0.8
38 self.show_artist()
39 self.add(self.screen_title)38 self.add(self.screen_title)
4039
41 #List indicator40 #List indicator
42 self.li = ListIndicator(0.74, 0.8, 0.2, 0.045, ListIndicator.VERTICAL)41 self.li = ListIndicator(0.74, 0.85, 0.2, 0.045, ListIndicator.VERTICAL)
43 self.li.set_maximum(self.track_menu.get_number_of_items())42 self.li.set_maximum(len(self.album.get_tracks()))
44 self.add(self.li)43 self.add(self.li)
4544
45 self.track_menu.active = True
46 self.track_menu.connect('selected', self._on_menu_selected)
47 self.track_menu.connect('moved', self._display_selected_track)
48
46 def _create_album_cover_texture(self):49 def _create_album_cover_texture(self):
47 """50 """
48 Create a texture that is displayed next to track list. This texture51 Create a texture that is displayed next to track list. This texture
@@ -83,31 +86,14 @@
83 """86 """
84 Create track menu. This menu contains list of all tracks on album.87 Create track menu. This menu contains list of all tracks on album.
85 """88 """
86 self.track_menu = TextMenu(self.theme, self.config.show_effects())89 menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
87 self.track_menu.set_row_count(1)
88 self.track_menu.set_visible_column_count(7)
89 self.track_menu.set_item_size(self.get_abs_x(0.4393),
90 self.get_abs_y(0.0781))
91 self.track_menu.set_position(self.get_abs_x(0.4978),
92 self.get_abs_y(0.2344))
9390
94 # Create menu items based on MusicLibrary
95 tracks = self.album.get_tracks()91 tracks = self.album.get_tracks()
96 for track in tracks:92 tracks_list = [[track.get_title(), track.get_length_string(), track] \
97 track_length = str(track.get_length() / 60) + ":" + \93 for track in tracks]
98 str(track.get_length() % 60).zfill(2)94 menu.async_add(tracks_list)
99 item = TextMenuItem(0.4393, 0.0781, track.get_title(), track_length)95
100 item.set_userdata(track)96 return menu
101 self.track_menu.add_actor(item)
102
103 self.track_menu.set_active(True)
104 self.add(self.track_menu)
105
106 def show_artist(self):
107 # Screen Title (Displayed at the bottom left corner)
108 track = self.track_menu.get_current_menuitem().get_userdata()
109 self.screen_title.set_text(track.get_artist())
110 self.screen_title.show()
11197
112 def is_interested_in_play_action(self):98 def is_interested_in_play_action(self):
113 """99 """
@@ -121,27 +107,32 @@
121 Override function from Screen class. See Screen class for107 Override function from Screen class. See Screen class for
122 better documentation.108 better documentation.
123 """109 """
124 track = self.track_menu.get_current_menuitem().get_userdata()110 track = self.track_menu.selected_userdata
125 self.media_player.set_media(track)111 self.media_player.set_media(track)
126 self.media_player.play()112 self.media_player.play()
127113
128 def _move_menu(self, menu_direction):
129 '''Move the text menu in the provided direction.'''
130 self.track_menu.move(menu_direction)
131 self.li.set_current(self.track_menu.get_current_position() + 1)
132 self.show_artist()
133
134 def _handle_up(self):114 def _handle_up(self):
135 '''Handle UserEvent.NAVIGATE_UP.'''115 '''Handle UserEvent.NAVIGATE_UP.'''
136 self._move_menu(TextMenu.UP)116 self.track_menu.up()
137117
138 def _handle_down(self):118 def _handle_down(self):
139 '''Handle UserEvent.NAVIGATE_DOWN.'''119 '''Handle UserEvent.NAVIGATE_DOWN.'''
140 self._move_menu(TextMenu.DOWN)120 self.track_menu.down()
141121
142 def _handle_select(self):122 def _handle_select(self, event=None):
143 '''Handle UserEvent.NAVIGATE_SELECT.'''123 '''Handle UserEvent.NAVIGATE_SELECT.'''
144 track = self.track_menu.get_current_menuitem().get_userdata()124 track = self.track_menu.selected_userdata
145 kwargs = { 'track' : track }125 kwargs = { 'track' : track }
146 self.callback("audio_play", kwargs)126 self.callback("audio_play", kwargs)
147127
128 def _on_menu_selected(self, actor=None):
129 '''Handle a *select command* if an item was selected.'''
130 self._handle_select()
131
132 def _display_selected_track(self, actor=None):
133 '''Update of the list indicator and the screen's title'''
134 self.li.set_current(self.track_menu.selected_index + 1)
135 track = self.track_menu.selected_userdata
136 self.screen_title.set_text(track.get_artist())
137 self.screen_title.show()
138
148139
=== modified file 'entertainerlib/frontend/gui/screens/disc.py'
--- entertainerlib/frontend/gui/screens/disc.py 2009-05-10 07:43:27 +0000
+++ entertainerlib/frontend/gui/screens/disc.py 2009-07-12 07:57:05 +0000
@@ -18,7 +18,6 @@
18 LoadingAnimation)18 LoadingAnimation)
19from entertainerlib.frontend.gui.widgets.texture import Texture19from entertainerlib.frontend.gui.widgets.texture import Texture
20from entertainerlib.frontend.gui.widgets.text_menu import TextMenu20from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
21from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
22from entertainerlib.frontend.medialibrary.playlist import Playlist21from entertainerlib.frontend.medialibrary.playlist import Playlist
23from entertainerlib.utils.albumart_downloader import AlbumArtDownloader22from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
2423
@@ -69,7 +68,8 @@
6968
70 self.playlist = Playlist(tracks)69 self.playlist = Playlist(tracks)
71 self._create_album_info(title, artist, tracks, disc.get_length())70 self._create_album_info(title, artist, tracks, disc.get_length())
72 self._create_track_menu(tracks)71 self.track_menu = self._create_track_menu(tracks)
72 self.add(self.track_menu)
73 self._create_album_cover_texture(artist, title)73 self._create_album_cover_texture(artist, title)
74 self._create_list_indicator()74 self._create_list_indicator()
7575
@@ -172,45 +172,37 @@
172 Create a track menu. This menu contains list of all tracks on album.172 Create a track menu. This menu contains list of all tracks on album.
173 @param tracks: List of CompactDisc objects173 @param tracks: List of CompactDisc objects
174 """174 """
175 self.track_menu = TextMenu(self.theme, self.config.show_effects())175 menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
176 self.track_menu.set_row_count(1)176 menu.visible_rows = 7
177 self.track_menu.set_visible_column_count(7)177
178 self.track_menu.set_item_size(self.get_abs_x(0.4393),178 tracks_list = [[track.get_title(), track.get_length_string(), index] \
179 self.get_abs_y(0.0781))179 for index, track in enumerate(tracks)]
180 self.track_menu.set_position(self.get_abs_x(0.4978),180 menu.async_add(tracks_list)
181 self.get_abs_y(0.2344))181
182182 menu.active = True
183 for index, track in enumerate(tracks):183 menu.connect('selected', self._handle_select)
184 length = str(track.get_length() / 60) + ":" + \184 menu.connect('moved', self._display_selected_track)
185 str(track.get_length() % 60).zfill(2)185
186 item = TextMenuItem(0.4393, 0.0781, track.get_title(), length)186 return menu
187 item.set_userdata(index)
188 self.track_menu.add_actor(item)
189 self.track_menu.set_active(True)
190 self.add(self.track_menu)
191187
192 def _create_list_indicator(self):188 def _create_list_indicator(self):
193 '''Create list indicator for track list.'''189 '''Create list indicator for track list.'''
194 self.li = ListIndicator(0.75, 0.8, 0.2, 0.045, ListIndicator.VERTICAL)190 self.li = ListIndicator(0.75, 0.8, 0.2, 0.045, ListIndicator.VERTICAL)
195 self.li.set_maximum(self.track_menu.get_number_of_items())191 tracks = self.music_library.get_compact_disc_information().get_tracks()
192 self.li.set_maximum(len(tracks))
196 self.add(self.li)193 self.add(self.li)
197194
198 def _move_menu(self, menu_direction):
199 '''Move the text menu in the provided direction.'''
200 self.track_menu.move(menu_direction)
201 self.li.set_current(self.track_menu.get_current_position() + 1)
202
203 def _handle_up(self):195 def _handle_up(self):
204 '''Handle UserEvent.NAVIGATE_UP.'''196 '''Handle UserEvent.NAVIGATE_UP.'''
205 self._move_menu(TextMenu.UP)197 self.track_menu.up()
206198
207 def _handle_down(self):199 def _handle_down(self):
208 '''Handle UserEvent.NAVIGATE_DOWN.'''200 '''Handle UserEvent.NAVIGATE_DOWN.'''
209 self._move_menu(TextMenu.DOWN)201 self.track_menu.down()
210202
211 def _handle_select(self):203 def _handle_select(self, event=None):
212 '''Handle UserEvent.NAVIGATE_SELECT.'''204 '''Handle UserEvent.NAVIGATE_SELECT.'''
213 track_index = self.track_menu.get_current_menuitem().get_userdata()205 track_index = self.track_menu.selected_userdata
214 self.playlist.set_current(track_index)206 self.playlist.set_current(track_index)
215 self.media_player.set_playlist(self.playlist)207 self.media_player.set_playlist(self.playlist)
216 self.media_player.play()208 self.media_player.play()
@@ -220,3 +212,7 @@
220 if self.has_disc:212 if self.has_disc:
221 Screen.handle_user_event(self, event)213 Screen.handle_user_event(self, event)
222214
215 def _display_selected_track(self, event=None):
216 '''Update of the list indicator.'''
217 self.li.set_current(self.track_menu.selected_index + 1)
218
223219
=== modified file 'entertainerlib/frontend/gui/screens/feed.py'
--- entertainerlib/frontend/gui/screens/feed.py 2009-06-25 19:06:28 +0000
+++ entertainerlib/frontend/gui/screens/feed.py 2009-07-12 07:57:05 +0000
@@ -8,7 +8,6 @@
8from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator8from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
9from entertainerlib.frontend.gui.widgets.texture import Texture9from entertainerlib.frontend.gui.widgets.texture import Texture
10from entertainerlib.frontend.gui.widgets.text_menu import TextMenu10from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
11from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
12from entertainerlib.utils.feed_utils import FeedEntryParser11from entertainerlib.utils.feed_utils import FeedEntryParser
1312
14class Feed(Screen):13class Feed(Screen):
@@ -21,7 +20,8 @@
21 self.feed = feed20 self.feed = feed
22 self.menu = None21 self.menu = None
2322
24 self._create_entry_menu()23 self.menu = self._create_entry_menu()
24 self.add(self.menu)
2525
26 # Screen Title (Displayed at the bottom left corner)26 # Screen Title (Displayed at the bottom left corner)
27 screen_title = Label(0.13, "screentitle", 0, 0.87, _("Feed"))27 screen_title = Label(0.13, "screentitle", 0, 0.87, _("Feed"))
@@ -56,44 +56,47 @@
5656
57 #List indicator57 #List indicator
58 self.li = ListIndicator(0.57, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)58 self.li = ListIndicator(0.57, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
59 self.li.set_maximum(self.menu.get_number_of_items())59 self.li.set_maximum(len(self.feed.entries))
60 self.add(self.li)60 self.add(self.li)
6161
62 self._display_selected_feed()
63
64 self.menu.connect('selected', self._on_menu_selected)
65 self.menu.connect('moved', self._display_selected_feed)
66
62 def _create_entry_menu(self):67 def _create_entry_menu(self):
63 """68 """Create Entries-menu. This menu contains list of entries."""
64 Create Entries-menu. This menu contains list of entries.69 menu = TextMenu(0.2438, 0.2604, 0.5124, 0.0781)
65 """70
66 self.menu = TextMenu(self.theme, self.config.show_effects())71 parser = FeedEntryParser()
67 self.menu.set_row_count(1)72 feeds = self.feed.entries
68 self.menu.set_item_size(self.get_abs_x(0.5124), self.get_abs_y(0.0781))73 feeds_list = [[parser.strip_tags(feed.title), None, feed] \
69 self.menu.set_position(self.get_abs_x(0.2438), self.get_abs_y(0.2604))74 for feed in feeds]
7075 menu.async_add(feeds_list)
71 # Create menu items based on FeedLibrary76
72 for entry in self.feed.entries:77 menu.active = True
73 item = TextMenuItem(0.5124, 0.0781,78
74 FeedEntryParser().strip_tags(entry.title))79 return menu
75 item.set_userdata(entry)
76 self.menu.add_actor(item)
77
78 self.menu.set_active(True)
79 self.add(self.menu)
80
81 def _move_menu(self, menu_direction):
82 '''Move the text menu in the given direction.'''
83 self.menu.move(menu_direction)
84 self.li.set_current(self.menu.get_current_position() + 1)
8580
86 def _handle_up(self):81 def _handle_up(self):
87 '''Handle UserEvent.NAVIGATE_UP.'''82 '''Handle UserEvent.NAVIGATE_UP.'''
88 self._move_menu(TextMenu.UP)83 self.menu.up()
8984
90 def _handle_down(self):85 def _handle_down(self):
91 '''Handle UserEvent.NAVIGATE_DOWN.'''86 '''Handle UserEvent.NAVIGATE_DOWN.'''
92 self._move_menu(TextMenu.DOWN)87 self.menu.down()
9388
94 def _handle_select(self):89 def _handle_select(self, event=None):
95 '''Handle UserEvent.NAVIGATE_SELECT.'''90 '''Handle UserEvent.NAVIGATE_SELECT.'''
96 entry = self.menu.get_current_menuitem().get_userdata()91 entry = self.menu.selected_userdata
97 kwargs = { 'feed' : self.feed, 'entry' : entry }92 kwargs = { 'feed' : self.feed, 'entry' : entry }
98 self.callback("entry", kwargs)93 self.callback("entry", kwargs)
9994
95 def _on_menu_selected(self, event=None):
96 '''Handle a *select command* if an item was selected.'''
97 self._handle_select()
98
99 def _display_selected_feed(self, event=None):
100 '''Update of the list indicator'''
101 self.li.set_current(self.menu.selected_index + 1)
102
100103
=== modified file 'entertainerlib/frontend/gui/screens/main.py'
--- entertainerlib/frontend/gui/screens/main.py 2009-06-28 20:12:13 +0000
+++ entertainerlib/frontend/gui/screens/main.py 2009-06-29 05:25:01 +0000
@@ -9,7 +9,6 @@
9from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu9from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu
10from entertainerlib.frontend.gui.widgets.texture import Texture10from entertainerlib.frontend.gui.widgets.texture import Texture
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
12from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
13from entertainerlib.utils.cd_utils import eject_cd12from entertainerlib.utils.cd_utils import eject_cd
14from entertainerlib.utils.feed_utils import FeedEntryParser13from entertainerlib.utils.feed_utils import FeedEntryParser
1514
@@ -47,13 +46,15 @@
4746
48 self.menu = self._create_main_menu()47 self.menu = self._create_main_menu()
49 self.add(self.menu)48 self.add(self.menu)
50 self.menu.connect('selected', self._on_menu_selected)49
50 self.add(ClockLabel(0.13, "screentitle", 0, 0.87))
51
52 self.menu.connect('selected', self._handle_select)
51 self.menu.connect('moved', self._on_menu_moved)53 self.menu.connect('moved', self._on_menu_moved)
52 self.menu.connect('activated', self._main_menu_activation)54 self.menu.connect('activated', self._on_menu_activated)
55
53 self.menu.active = True56 self.menu.active = True
5457
55 self.add(ClockLabel(0.13, "screentitle", 0, 0.87))
56
57 def get_type(self):58 def get_type(self):
58 """Return screen type."""59 """Return screen type."""
59 return Screen.NORMAL60 return Screen.NORMAL
@@ -96,24 +97,18 @@
96 '''Create the RSS preview menu that will show feed highlights. An97 '''Create the RSS preview menu that will show feed highlights. An
97 uninitialized menu will be returned to prevent move errors if there98 uninitialized menu will be returned to prevent move errors if there
98 is nothing in the feed library.'''99 is nothing in the feed library.'''
99 menu = TextMenu(self.theme, self.config.show_effects())100 menu = TextMenu(0.035, 0.12, 0.549, 0.078)
100101
101 if self.feed_library.is_empty() is False:102 if self.feed_library.is_empty() is False:
102 menu.set_row_count(1)103 parser = FeedEntryParser()
103 menu.set_position(self.get_abs_x(0.035), self.get_abs_y(0.12))
104 menu.set_item_size(self.get_abs_x(0.549), self.get_abs_y(0.078))
105
106 # List of latest entries. pack = (Feed object, Entry object)
107 entries = self.feed_library.get_latest_entries(5)104 entries = self.feed_library.get_latest_entries(5)
108 for pack in entries:105 entries_list = [[parser.strip_tags(entry[0].title + " - " + \
109 text = pack[0].title + " - " + pack[1].title106 entry[1].title), entry[1].date, \
110 item = TextMenuItem(0.549, 0.078,107 { 'feed' : entry[0], 'entry' : entry[1] }] for entry in entries]
111 FeedEntryParser().strip_tags(text), pack[1].date)108 menu.async_add(entries_list)
112 kwargs = { 'feed' : pack[0], 'entry' : pack[1] }
113 item.set_userdata(kwargs)
114 menu.add_actor(item)
115109
116 menu.set_active(False)110 menu.connect('selected', self._handle_select)
111 menu.connect('activated', self._on_rss_menu_activated)
117112
118 return menu113 return menu
119114
@@ -198,11 +193,15 @@
198 # No headlines available in the library193 # No headlines available in the library
199 info = Label(0.05, "title", 0.1, 0.35, _("No headlines available"))194 info = Label(0.05, "title", 0.1, 0.35, _("No headlines available"))
200 preview.add(info)195 preview.add(info)
196 self.rss_preview_menu = None
201197
202 return preview198 return preview
203199
204 def _update_preview_area(self):200 def _update_preview_area(self):
205 '''Update the preview area to display the current menu item.'''201 '''Update the preview area to display the current menu item.'''
202 if self.rss_preview_menu:
203 rss_preview_menu_active = self.rss_preview_menu.active
204
206 self.preview.remove_all()205 self.preview.remove_all()
207 item = self.menu.get_selected()206 item = self.menu.get_selected()
208207
@@ -214,10 +213,8 @@
214 self.preview.add(self._create_playing_preview())213 self.preview.add(self._create_playing_preview())
215 elif item.get_name() == "rss":214 elif item.get_name() == "rss":
216 self.preview.add(self._create_rss_preview())215 self.preview.add(self._create_rss_preview())
217 # When we update the rss preview menu, we must set it active if216 if self.rss_preview_menu:
218 # main menu is not.217 self.rss_preview_menu.active = rss_preview_menu_active
219 if not self.menu.active:
220 self.rss_preview_menu.set_active(True)
221 else:218 else:
222 update = False219 update = False
223220
@@ -225,7 +222,7 @@
225 if update:222 if update:
226 fade_in = clutter.Timeline(20, 60)223 fade_in = clutter.Timeline(20, 60)
227 alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)224 alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)
228 self.behaviour = clutter.BehaviourOpacity(0x00, 0xff, alpha_in)225 self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
229 self.behaviour.apply(self.preview)226 self.behaviour.apply(self.preview)
230 fade_in.start()227 fade_in.start()
231228
@@ -257,25 +254,25 @@
257 if self.menu.active:254 if self.menu.active:
258 self.menu.scroll_up()255 self.menu.scroll_up()
259 else:256 else:
260 self.rss_preview_menu.move(TextMenu.UP)257 self.rss_preview_menu.up()
261258
262 def _handle_down(self):259 def _handle_down(self):
263 '''Handle UserEvent.NAVIGATE_DOWN.'''260 '''Handle UserEvent.NAVIGATE_DOWN.'''
264 if self.menu.active:261 if self.menu.active:
265 self.menu.scroll_down()262 self.menu.scroll_down()
266 else:263 else:
267 self.rss_preview_menu.move(TextMenu.DOWN)264 self.rss_preview_menu.down()
268265
269 def _handle_left(self):266 def _handle_left(self):
270 '''Handle UserEvent.NAVIGATE_LEFT.'''267 '''Handle UserEvent.NAVIGATE_LEFT.'''
271 if self._can_move_horizontally():268 if self._can_move_horizontally():
272 self._preview_activation()269 self.rss_preview_menu.active = True
273270
274 def _handle_right(self):271 def _handle_right(self):
275 '''Handle UserEvent.NAVIGATE_RIGHT.'''272 '''Handle UserEvent.NAVIGATE_RIGHT.'''
276 self.menu.active = True273 self.menu.active = True
277274
278 def _handle_select(self):275 def _handle_select(self, event=None):
279 '''Handle UserEvent.NAVIGATE_SELECT.'''276 '''Handle UserEvent.NAVIGATE_SELECT.'''
280 item = self.menu.get_selected()277 item = self.menu.get_selected()
281278
@@ -297,27 +294,20 @@
297 if self.menu.active:294 if self.menu.active:
298 self.callback("rss")295 self.callback("rss")
299 else:296 else:
300 menu_item = self.rss_preview_menu.get_current_menuitem()297 menu_item = self.rss_preview_menu.selected_item
301 kwargs = menu_item.get_userdata()298 kwargs = menu_item.userdata
302 self.callback("entry", kwargs)299 self.callback("entry", kwargs)
303300
304 def _on_menu_moved(self, actor):301 def _on_menu_moved(self, event):
305 '''Update preview area when selected item changed on the menu.'''302 '''Update preview area when selected item changed on the menu.'''
306 self._update_preview_area()303 self._update_preview_area()
307304
308 def _on_menu_selected(self, actor):305 def _on_menu_activated(self, event=None):
309 '''Handle a *select command* if an item was selected.'''
310 self._handle_select()
311
312 def _main_menu_activation(self, actor=None):
313 '''Handle the main menu activation.'''306 '''Handle the main menu activation.'''
314 self.menu.active = True307 if self.rss_preview_menu is not None:
315 if self.rss_preview_menu:308 self.rss_preview_menu.active = False
316 self.rss_preview_menu.set_active(False)
317309
318 def _preview_activation(self, actor=None):310 def _on_rss_menu_activated(self, event=None):
319 '''Handle the preview activation.'''311 '''Handle the rss menu activation.'''
320 self.menu.active = False312 self.menu.active = False
321 if self.rss_preview_menu:
322 self.rss_preview_menu.set_active(True)
323313
324314
=== modified file 'entertainerlib/frontend/gui/screens/movie.py'
--- entertainerlib/frontend/gui/screens/movie.py 2009-06-05 05:10:21 +0000
+++ entertainerlib/frontend/gui/screens/movie.py 2009-07-12 07:57:05 +0000
@@ -13,7 +13,6 @@
13from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea13from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.frontend.gui.widgets.texture import Texture
15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
16from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1716
18class Movie(Screen):17class Movie(Screen):
19 '''Screen contains information of one movie.'''18 '''Screen contains information of one movie.'''
@@ -156,47 +155,40 @@
156 """155 """
157 Create menu of the screen. This displayed the left side of the screen.156 Create menu of the screen. This displayed the left side of the screen.
158 """157 """
159 self.menu = TextMenu(self.theme, self.config.show_effects())158 self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781)
160 self.menu.set_row_count(1)159 self.menu.add_item(_("Watch"), None, "watch")
161 #self.track_menu.set_visible_column_count(10)160 self.menu.active = True
162 self.menu.set_item_size(self.get_abs_x(0.2196), self.get_abs_y(0.0781))161
163 self.menu.set_position(self.get_abs_x(0.07), self.get_abs_y(0.1))
164
165 item1 = TextMenuItem(0.2196, 0.0781, _("Watch"))
166 item1.set_userdata("watch")
167 self.menu.add_actor(item1)
168
169 self.menu.set_active(True)
170 self.add(self.menu)162 self.add(self.menu)
171163
172 def _handle_up(self):164 def _handle_up(self):
173 '''Handle UserEvent.NAVIGATE_UP.'''165 '''Handle UserEvent.NAVIGATE_UP.'''
174 if self.menu.is_active():166 if self.menu.active:
175 self.menu.move(self.menu.UP)167 self.menu.up()
176 else:168 else:
177 self.scroll_area.scroll_up()169 self.scroll_area.scroll_up()
178170
179 def _handle_down(self):171 def _handle_down(self):
180 '''Handle UserEvent.NAVIGATE_DOWN.'''172 '''Handle UserEvent.NAVIGATE_DOWN.'''
181 if self.menu.is_active():173 if self.menu.active:
182 self.menu.move(self.menu.DOWN)174 self.menu.down()
183 else:175 else:
184 self.scroll_area.scroll_down()176 self.scroll_area.scroll_down()
185177
186 def _handle_left(self):178 def _handle_left(self):
187 '''Handle UserEvent.NAVIGATE_LEFT.'''179 '''Handle UserEvent.NAVIGATE_LEFT.'''
188 self.menu.set_active(True)180 self.menu.active = True
189 self.scroll_area.set_active(False)181 self.scroll_area.set_active(False)
190182
191 def _handle_right(self):183 def _handle_right(self):
192 '''Handle UserEvent.NAVIGATE_RIGHT.'''184 '''Handle UserEvent.NAVIGATE_RIGHT.'''
193 self.menu.set_active(False)185 self.menu.active = False
194 self.scroll_area.set_active(True)186 self.scroll_area.set_active(True)
195187
196 def _handle_select(self):188 def _handle_select(self, event=None):
197 '''Handle UserEvent.NAVIGATE_SELECT.'''189 '''Handle UserEvent.NAVIGATE_SELECT.'''
198 if self.menu.is_active():190 if self.menu.active:
199 item = self.menu.get_current_menuitem().get_userdata()191 item = self.menu.selected_userdata
200 if item == "watch":192 if item == "watch":
201 self.media_player.set_media(self.movie)193 self.media_player.set_media(self.movie)
202 self.media_player.play()194 self.media_player.play()
203195
=== modified file 'entertainerlib/frontend/gui/screens/photo.py'
--- entertainerlib/frontend/gui/screens/photo.py 2009-05-10 06:24:43 +0000
+++ entertainerlib/frontend/gui/screens/photo.py 2009-05-21 16:12:09 +0000
@@ -182,7 +182,7 @@
182 else:182 else:
183 self.texture.move_by(-self.MOVE_SIZE, 0)183 self.texture.move_by(-self.MOVE_SIZE, 0)
184184
185 def _handle_select(self):185 def _handle_select(self, event=None):
186 '''Handle UserEvent.NAVIGATE_SELECT.'''186 '''Handle UserEvent.NAVIGATE_SELECT.'''
187 # Zoom image. If we zoom then we stop sliding photos.187 # Zoom image. If we zoom then we stop sliding photos.
188 self.stop_slideshow()188 self.stop_slideshow()
189189
=== modified file 'entertainerlib/frontend/gui/screens/photo_albums.py'
--- entertainerlib/frontend/gui/screens/photo_albums.py 2009-05-10 07:43:27 +0000
+++ entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-12 07:57:05 +0000
@@ -13,7 +13,6 @@
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
14from entertainerlib.frontend.gui.widgets.texture import Texture14from entertainerlib.frontend.gui.widgets.texture import Texture
15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu15from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
16from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1716
18class PhotoAlbums(Screen):17class PhotoAlbums(Screen):
19 '''Screen contains a list of photo albums and album previews.'''18 '''Screen contains a list of photo albums and album previews.'''
@@ -46,10 +45,16 @@
46 self.in_opacity = None45 self.in_opacity = None
47 self.out_opacity = None46 self.out_opacity = None
48 self.preview_textures = None47 self.preview_textures = None
49 self._create_album_menu()48 self.menu = self._create_album_menu()
49 self.add(self.menu)
50 self.li = None50 self.li = None
51 self._create_list_indicator()51 self._create_list_indicator()
5252
53 self._update_album_preview(self.menu.selected_userdata)
54
55 self.menu.connect('selected', self._handle_select)
56 self.menu.connect('moved', self._display_selected_album)
57
53 def _create_no_photos_information(self):58 def _create_no_photos_information(self):
54 """59 """
55 Create textures and labels for information screen. This is displayed60 Create textures and labels for information screen. This is displayed
@@ -80,28 +85,17 @@
80 Create ImageAlbum-menu. This menu contains list of albums. It also85 Create ImageAlbum-menu. This menu contains list of albums. It also
81 displays number of photographs per album.86 displays number of photographs per album.
82 """87 """
83 self.menu = TextMenu(self.theme, self.config.show_effects())88 menu = TextMenu(0.5271, 0.3385, 0.4393, 0.0781)
84 self.menu.set_row_count(1)89 menu.visible_rows = 7
85 self.menu.set_visible_column_count(7)
86 self.menu.set_item_size(self.get_abs_x(0.4393), self.get_abs_y(0.0781))
87 self.menu.set_position(self.get_abs_x(0.5271), self.get_abs_y(0.3385))
8890
89 # Create menu items based on ImageLibrary
90 albums = self.image_library.get_albums()91 albums = self.image_library.get_albums()
91 for album in albums:92 albums_list = [[album.get_title(), str(album.get_number_of_images()),
92 nro_of_photos = album.get_number_of_images()93 album] for album in albums if album.get_number_of_images() != 0]
93 if nro_of_photos == 0: # Skip empty albums94 menu.async_add(albums_list)
94 continue95
95 else:96 menu.active = True
96 nro_of_photos = str(nro_of_photos)97
97 item = TextMenuItem(0.4393, 0.0781, album.get_title(),98 return menu
98 nro_of_photos)
99 item.set_userdata(album)
100 self.menu.add_actor(item)
101
102 self.add(self.menu)
103 self._update_album_preview(
104 self.menu.get_current_menuitem().get_userdata())
10599
106 def _create_album_preview(self, album):100 def _create_album_preview(self, album):
107 """101 """
@@ -246,30 +240,20 @@
246 def _create_list_indicator(self):240 def _create_list_indicator(self):
247 '''Create list indicator for albums list.'''241 '''Create list indicator for albums list.'''
248 self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)242 self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
249 self.li.set_maximum(self.menu.get_number_of_items())243 self.li.set_maximum(len(self.image_library.get_albums()))
250 self.add(self.li)244 self.add(self.li)
251245
252 def _move_menu(self, menu_direction):
253 '''Move the menu in the given direction.'''
254 self.menu.move(menu_direction)
255 self.li.set_current(self.menu.get_current_position() + 1)
256 # 500ms timeout before preview is updated (fast scrolling)
257 if self.timeout is not None:
258 gobject.source_remove(self.timeout)
259 self.timeout = gobject.timeout_add(500, self._update_album_preview,
260 self.menu.get_current_menuitem().get_userdata())
261
262 def _handle_up(self):246 def _handle_up(self):
263 '''Handle UserEvent.NAVIGATE_UP.'''247 '''Handle UserEvent.NAVIGATE_UP.'''
264 self._move_menu(TextMenu.UP)248 self.menu.up()
265249
266 def _handle_down(self):250 def _handle_down(self):
267 '''Handle UserEvent.NAVIGATE_DOWN.'''251 '''Handle UserEvent.NAVIGATE_DOWN.'''
268 self._move_menu(TextMenu.DOWN)252 self.menu.down()
269253
270 def _handle_select(self):254 def _handle_select(self, event=None):
271 '''Handle UserEvent.NAVIGATE_SELECT.'''255 '''Handle UserEvent.NAVIGATE_SELECT.'''
272 album = self.menu.get_current_menuitem().get_userdata()256 album = self.menu.selected_userdata
273 kwargs = { 'title' : album.get_title(), 'images' : album.get_images() }257 kwargs = { 'title' : album.get_title(), 'images' : album.get_images() }
274 self.callback("photographs", kwargs)258 self.callback("photographs", kwargs)
275259
@@ -280,3 +264,12 @@
280 else:264 else:
281 Screen.handle_user_event(self, event)265 Screen.handle_user_event(self, event)
282266
267 def _display_selected_album(self, event=None):
268 '''Update of the list indicator'''
269 self.li.set_current(self.menu.selected_index + 1)
270 # 500ms timeout before preview is updated (fast scrolling)
271 if self.timeout is not None:
272 gobject.source_remove(self.timeout)
273 self.timeout = gobject.timeout_add(500, self._update_album_preview,
274 self.menu.selected_userdata)
275
283276
=== modified file 'entertainerlib/frontend/gui/screens/photographs.py'
--- entertainerlib/frontend/gui/screens/photographs.py 2009-05-10 07:43:27 +0000
+++ entertainerlib/frontend/gui/screens/photographs.py 2009-07-14 11:06:21 +0000
@@ -2,13 +2,11 @@
2'''Photographs - Screen contains a list/grid of photographs'''2'''Photographs - Screen contains a list/grid of photographs'''
33
4import pango4import pango
5import gobject
65
7from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.frontend.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
9from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.frontend.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
11from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem
12from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.frontend.gui.widgets.loading_animation import (
13 LoadingAnimation)11 LoadingAnimation)
14from entertainerlib.frontend.gui.widgets.texture import Texture12from entertainerlib.frontend.gui.widgets.texture import Texture
@@ -19,7 +17,6 @@
19 def __init__(self, move_to_new_screen_callback, title, images):17 def __init__(self, move_to_new_screen_callback, title, images):
20 Screen.__init__(self, 'Photographs', move_to_new_screen_callback)18 Screen.__init__(self, 'Photographs', move_to_new_screen_callback)
2119
22 self.theme = self.config.theme
23 self.images = images20 self.images = images
2421
25 # Screen Title (Displayed at the bottom left corner)22 # Screen Title (Displayed at the bottom left corner)
@@ -46,54 +43,32 @@
46 self._create_list_indicator()43 self._create_list_indicator()
4744
48 # Create photomenu45 # Create photomenu
49 self.menu = ImageMenu(self.theme)46 self.menu = ImageMenu(0.03, 0.08, 0.12, self.y_for_x(0.12))
50 self.menu.set_visible_column_count(6)47 self.menu.items_per_col = 3
51 self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.1823))48 self.menu.visible_rows = 3
5249 self.menu.visible_cols = 8
53 if self.config.show_effects():50 self.menu.active = True
54 self.menu.set_animate(True)
55
56 self.add(self.menu)51 self.add(self.menu)
5752
58 photos = self.images53 photos = self.images
59 self.behaviour = None54 photos_list = [[Texture(photo.get_thumbnail_url()), photo] \
60 gobject.timeout_add(25, self._create_photo_menuitems, photos)55 for photo in photos]
6156 self.menu.async_add(photos_list)
62 self._update_image_info(0)57
6358 self.menu.connect("selected", self._handle_select)
64 def _create_photo_menuitems(self, photographs):59 self.menu.connect('moved', self._update_image_info)
65 """60 self.menu.connect("filled", self._on_menu_filled)
66 This function is called as async callback. It is also recursive61
67 function. This allows us load menu items in the background without62 def _update_image_info(self, event=None):
68 blocking the user interface.63 """Update image information box."""
69 """64 image = self.images[self.menu.selected_index]
70 # Check if we should stop recursive calls (menu is complete)
71 if len(photographs) > 0:
72 photo = photographs[0]
73 texture = Texture(photo.get_thumbnail_url())
74 item = ImageMenuItem(0.1464, 0.75, texture)
75 item.set_userdata(photo)
76 self.menu.add_actor(item)
77 # Recursive call, remove first artist from the list
78 gobject.timeout_add(15, self._create_photo_menuitems,
79 photographs[1:])
80 return False
81 else:
82 self.throbber.hide()
83 return False
84
85 def _update_image_info(self, number):
86 """
87 Update image information box.
88 @param number: Number of image. This is index of the self.__images
89 """
90 image = self.images[number]
91 name = image.get_title()65 name = image.get_title()
92 desc = image.get_description()66 desc = image.get_description()
93 self.image_title.set_text(name)67 self.image_title.set_text(name)
94 self.image_title.set_size(0.366, 0.04167)68 self.image_title.set_size(0.366, 0.04167)
95 self.image_desc.set_text(desc)69 self.image_desc.set_text(desc)
96 self.image_desc.set_size(0.366, 0.0911)70 self.image_desc.set_size(0.366, 0.0911)
71 self.li.set_current(self.menu.selected_index + 1)
9772
98 def _create_list_indicator(self):73 def _create_list_indicator(self):
99 '''Create list indicator for albums list.'''74 '''Create list indicator for albums list.'''
@@ -102,31 +77,29 @@
102 self.li.set_maximum(len(self.images))77 self.li.set_maximum(len(self.images))
103 self.add(self.li)78 self.add(self.li)
10479
105 def _move_menu(self, menu_direction):
106 '''Move the menu in the given direction.'''
107 self.menu.move(menu_direction)
108 self._update_image_info(self.menu.get_current_position())
109 self.li.set_current(self.menu.get_current_position() + 1)
110
111 def _handle_up(self):80 def _handle_up(self):
112 '''Handle UserEvent.NAVIGATE_UP.'''81 '''Handle UserEvent.NAVIGATE_UP.'''
113 self._move_menu(ImageMenu.UP)82 self.menu.up()
11483
115 def _handle_down(self):84 def _handle_down(self):
116 '''Handle UserEvent.NAVIGATE_DOWN.'''85 '''Handle UserEvent.NAVIGATE_DOWN.'''
117 self._move_menu(ImageMenu.DOWN)86 self.menu.down()
11887
119 def _handle_left(self):88 def _handle_left(self):
120 '''Handle UserEvent.NAVIGATE_LEFT.'''89 '''Handle UserEvent.NAVIGATE_LEFT.'''
121 self._move_menu(ImageMenu.LEFT)90 self.menu.left()
12291
123 def _handle_right(self):92 def _handle_right(self):
124 '''Handle UserEvent.NAVIGATE_RIGHT.'''93 '''Handle UserEvent.NAVIGATE_RIGHT.'''
125 self._move_menu(ImageMenu.RIGHT)94 self.menu.right()
12695
127 def _handle_select(self):96 def _handle_select(self, event=None):
128 '''Handle UserEvent.NAVIGATE_SELECT.'''97 '''Handle UserEvent.NAVIGATE_SELECT.'''
129 index = self.menu.get_current_position()98 index = self.menu.selected_index
130 kwargs = {'current_photo_index' : index, 'images' : self.images}99 kwargs = {'current_photo_index' : index, 'images' : self.images}
131 self.callback("photo", kwargs)100 self.callback("photo", kwargs)
132101
102 def _on_menu_filled(self, event=None):
103 '''Handles filled event.'''
104 self.throbber.hide()
105
133106
=== modified file 'entertainerlib/frontend/gui/screens/question.py'
--- entertainerlib/frontend/gui/screens/question.py 2009-06-02 01:16:03 +0000
+++ entertainerlib/frontend/gui/screens/question.py 2009-06-27 09:03:33 +0000
@@ -4,7 +4,6 @@
4from entertainerlib.frontend.gui.screens.screen import Screen4from entertainerlib.frontend.gui.screens.screen import Screen
5from entertainerlib.frontend.gui.widgets.label import Label5from entertainerlib.frontend.gui.widgets.label import Label
6from entertainerlib.frontend.gui.widgets.text_menu import TextMenu6from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
7from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
87
9class Question(Screen):8class Question(Screen):
10 '''Screen is displayed when the application needs to ask a close ended9 '''Screen is displayed when the application needs to ask a close ended
@@ -32,18 +31,14 @@
3231
33 def display_answers(self):32 def display_answers(self):
34 '''Display a menu with answers on the screen.'''33 '''Display a menu with answers on the screen.'''
35 self.menu = TextMenu(self.config.theme, self.config.show_effects())34 self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781)
36 self.menu.set_name("questionmenu")35 self.menu.set_name("questionmenu")
37 self.menu.set_row_count(1)36 self.menu.connect('selected', self._handle_select)
38 self.menu.set_visible_column_count(7)
39 self.menu.set_item_size(self.get_abs_x(0.4393), self.get_abs_y(0.0781))
40 self.menu.set_position(self.get_abs_x(0.095), self.get_abs_y(0.5))
4137
42 for answer in self.answers:38 for answer in self.answers:
43 item = TextMenuItem(0.4173, 0.0781, str(answer))39 self.menu.add_item(str(answer), None, str(answer))
44 item.set_userdata(str(answer))
45 self.menu.add_actor(item)
4640
41 self.menu.active = True
47 self.add(self.menu)42 self.add(self.menu)
4843
49 def question_callback(self, selected_answer):44 def question_callback(self, selected_answer):
@@ -63,20 +58,16 @@
63 '''Go back to previous screen'''58 '''Go back to previous screen'''
64 self.move_to_previous_screen_callback()59 self.move_to_previous_screen_callback()
6560
66 def _move_menu(self, menu_direction):
67 '''Move the menu in the given direction.'''
68 self.menu.move(menu_direction)
69
70 def _handle_up(self):61 def _handle_up(self):
71 '''Handle UserEvent.NAVIGATE_UP.'''62 '''Handle UserEvent.NAVIGATE_UP.'''
72 self._move_menu(TextMenu.UP)63 self.menu.up()
7364
74 def _handle_down(self):65 def _handle_down(self):
75 '''Handle UserEvent.NAVIGATE_DOWN.'''66 '''Handle UserEvent.NAVIGATE_DOWN.'''
76 self._move_menu(TextMenu.DOWN)67 self.menu.down()
7768
78 def _handle_select(self):69 def _handle_select(self, event=None):
79 '''Handle UserEvent.NAVIGATE_SELECT.'''70 '''Handle UserEvent.NAVIGATE_SELECT.'''
80 self.question_callback(self.menu.get_current_menuitem().get_userdata())71 self.question_callback(self.menu.selected_userdata)
81 self.go_back()72 self.go_back()
8273
8374
=== modified file 'entertainerlib/frontend/gui/screens/rss.py'
--- entertainerlib/frontend/gui/screens/rss.py 2009-06-25 19:06:28 +0000
+++ entertainerlib/frontend/gui/screens/rss.py 2009-07-14 11:06:21 +0000
@@ -5,13 +5,9 @@
55
6from entertainerlib.frontend.gui.screens.screen import Screen6from entertainerlib.frontend.gui.screens.screen import Screen
7from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.frontend.gui.widgets.label import Label
8from entertainerlib.frontend.gui.widgets.menu import Menu
9from entertainerlib.frontend.gui.widgets.selector import Selector
10from entertainerlib.frontend.gui.widgets.texture import Texture8from entertainerlib.frontend.gui.widgets.texture import Texture
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu9from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
12from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
14from entertainerlib.frontend.medialibrary.feeds import Feed
15from entertainerlib.utils.feed_utils import FeedEntryParser11from entertainerlib.utils.feed_utils import FeedEntryParser
1612
17class Rss(Screen):13class Rss(Screen):
@@ -21,9 +17,9 @@
21 Screen.__init__(self, 'Rss', move_to_new_screen_callback)17 Screen.__init__(self, 'Rss', move_to_new_screen_callback)
2218
23 self.feed_order = 'UNREAD' # Feeds ordered by number of unread entries19 self.feed_order = 'UNREAD' # Feeds ordered by number of unread entries
24 self.active_menu = "feed"
25 self.theme = self.config.theme20 self.theme = self.config.theme
26 self.library = feed_library21 self.library = feed_library
22 self.parser = FeedEntryParser()
2723
28 # Screen Title (Displayed at the bottom left corner)24 # Screen Title (Displayed at the bottom left corner)
29 screen_title = Label(0.13, "screentitle", 0, 0.87, _("Headlines"))25 screen_title = Label(0.13, "screentitle", 0, 0.87, _("Headlines"))
@@ -47,11 +43,12 @@
47 else:43 else:
48 # Menus44 # Menus
49 self._create_context_menu()45 self._create_context_menu()
50 self._create_feed_menu()46 self.menu = self._create_feed_menu()
47 self.add(self.menu)
51 self.li = None48 self.li = None
52 self._create_feed_menu_indicator()49 self._create_feed_menu_indicator()
53 self.menu.set_active(True)50 self.menu.active = True
54 self.context_menu.set_active(False)51 self.context_menu.active = False
5552
56 # Feeds Title53 # Feeds Title
57 feeds_title = Label(0.04167, "title", 0.3953, 0.2, _("Feeds"))54 feeds_title = Label(0.04167, "title", 0.3953, 0.2, _("Feeds"))
@@ -62,45 +59,41 @@
62 self.add(unread_title)59 self.add(unread_title)
6360
64 # Feed description text61 # Feed description text
65 desc_text = FeedEntryParser().strip_tags(62 desc_text = self.parser.strip_tags(
66 Feed(self.menu.get_current_menuitem().get_userdata()63 self.menu.selected_userdata.description)
67 ).description)64
68 self.description = Label(0.03646, "text", 0.3953, 0.9, desc_text)65 self.description = Label(0.03646, "text", 0.3953, 0.9, desc_text)
69 self.description.set_ellipsize(pango.ELLIPSIZE_END)66 self.description.set_ellipsize(pango.ELLIPSIZE_END)
70 self.description.set_use_markup(True)67 self.description.set_use_markup(True)
71 self.description.set_size(0.5088, 0.0651)68 self.description.set_size(0.5088, 0.0651)
72 self.add(self.description)69 self.add(self.description)
7370
71 self.menu.connect('selected', self._handle_select)
72 self.menu.connect('moved', self._display_selected_feed)
73 self.menu.connect('activated', self._menu_activation)
74 self.context_menu.connect('selected', self._handle_select)
75 self.context_menu.connect('activated',
76 self._context_menu_activation)
77
74 def _create_feed_menu(self):78 def _create_feed_menu(self):
75 """79 """
76 Create Feed-menu. This menu contains list of Feeds. It also displays80 Create Feed-menu. This menu contains list of Feeds. It also displays
77 number of unread entries per feed.81 number of unread entries per feed.
78 """82 """
79 self.menu = TextMenu(self.theme, self.config.show_effects())83 menu = TextMenu(0.3807, 0.2604, 0.5124, 0.0781)
80 self.menu.set_row_count(1)
81 self.menu.set_visible_column_count(7)
82 self.menu.set_item_size(self.get_abs_x(0.5124), self.get_abs_y(0.0781))
83 self.menu.set_position(self.get_abs_x(0.3807), self.get_abs_y(0.2604))
8484
85 # Create menu items based on FeedLibrary
86 feeds = self.library.get_feeds(self.feed_order)85 feeds = self.library.get_feeds(self.feed_order)
87 for feed in feeds:86 feeds_list = [[self.parser.strip_tags(feed.title), \
88 unread = feed.get_number_of_unread()87 str(feed.get_number_of_unread()), feed] \
89 if unread == 0:88 for feed in feeds]
90 unread = None89 menu.async_add(feeds_list)
91 else:
92 unread = str(unread)
93 item = TextMenuItem(0.5124, 0.0781,
94 FeedEntryParser().strip_tags(feed.title), unread)
95 item.set_userdata(feed.url)
96 self.menu.add_actor(item)
9790
98 self.add(self.menu)91 return menu
9992
100 def _create_feed_menu_indicator(self):93 def _create_feed_menu_indicator(self):
101 '''Create a ListIndicator for feed menu.'''94 '''Create a ListIndicator for feed menu.'''
102 self.li = ListIndicator(0.7, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)95 self.li = ListIndicator(0.7, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
103 self.li.set_maximum(self.menu.get_number_of_items())96 self.li.set_maximum(self.menu.count)
104 self.add(self.li)97 self.add(self.li)
10598
106 def _create_context_menu(self):99 def _create_context_menu(self):
@@ -108,24 +101,14 @@
108 Create RSS-feed context menu and add it to the screen.101 Create RSS-feed context menu and add it to the screen.
109 This menu contains buttons for update and sorting.102 This menu contains buttons for update and sorting.
110 """103 """
111 select = Selector(Selector.LARGE, self.theme)104 self.context_menu = TextMenu(0.0732, 0.2604, 0.2196, 0.0781)
112 self.context_menu = Menu()105
113106 self.context_menu.add_item(_("Update feeds"))
114 self.context_menu.set_loop(True)107 self.context_menu.add_item(_("Mark all as read"))
115 item1 = TextMenuItem(0.2196, 0.0781, _("Update feeds"))108 self.context_menu.add_item(_("Sort by name"))
116 self.context_menu.add(item1)109 self.context_menu.add_item(_("Sort by unread"))
117 item2 = TextMenuItem(0.2196, 0.0781, _("Mark all as read"))110
118 self.context_menu.add(item2)111 self.context_menu.active = False
119 item3 = TextMenuItem(0.2196, 0.0781, _("Sort by name"))
120 self.context_menu.add(item3)
121 item4 = TextMenuItem(0.2196, 0.0781, _("Sort by unread"))
122 self.context_menu.add(item4)
123
124 self.context_menu.set_selector(select)
125 self.context_menu.set_position(self.get_abs_x(0.0732),
126 self.get_abs_y(0.2604))
127 self.context_menu.set_active(False)
128 self.context_menu.show_all()
129 self.add(self.context_menu)112 self.add(self.context_menu)
130113
131 def _create_no_feeds_information(self):114 def _create_no_feeds_information(self):
@@ -159,64 +142,41 @@
159 Update screen widgets. This is called always when screen is poped from142 Update screen widgets. This is called always when screen is poped from
160 the screen history.143 the screen history.
161 """144 """
162 #remove current feed menu, create a new one and set it to active145 feeds = self.library.get_feeds(self.feed_order)
163 selected_name = self.menu.get_current_menuitem().label.get_text()146 for idx, item in enumerate(self.menu.items):
164147 feed = feeds[idx]
165 self.remove(self.menu)148 item.userdata = feed
166 self._create_feed_menu()149 item.update(self.parser.strip_tags(feed.title),
167 self.menu.set_active(True)150 str(feed.get_number_of_unread()))
168
169 index = self.menu.get_index(selected_name)
170 if not index == -1:
171 self.menu.select(index)
172
173 def _move_menu(self, menu_direction):
174 '''Move the menu in the given direction.'''
175 if menu_direction == TextMenu.UP:
176 scroll_direction = self.context_menu.scroll_up
177 elif menu_direction == TextMenu.DOWN:
178 scroll_direction = self.context_menu.scroll_down
179
180 if self.active_menu == "feed":
181 self.menu.move(menu_direction)
182 self.li.set_current(self.menu.get_current_position() + 1)
183 feed = Feed(self.menu.get_current_menuitem().get_userdata())
184 self.description.set_text(FeedEntryParser().strip_tags(
185 feed.description))
186 self.description.set_size(0.5088, 0.0651)
187 else:
188 scroll_direction()
189151
190 def _handle_up(self):152 def _handle_up(self):
191 '''Handle UserEvent.NAVIGATE_UP.'''153 '''Handle UserEvent.NAVIGATE_UP.'''
192 self._move_menu(TextMenu.UP)154 if self.menu.active:
155 self.menu.up()
156 else:
157 self.context_menu.up()
193158
194 def _handle_down(self):159 def _handle_down(self):
195 '''Handle UserEvent.NAVIGATE_DOWN.'''160 '''Handle UserEvent.NAVIGATE_DOWN.'''
196 self._move_menu(TextMenu.DOWN)161 if self.menu.active:
162 self.menu.down()
163 else:
164 self.context_menu.down()
197165
198 def _handle_left(self):166 def _handle_left(self):
199 '''Handle UserEvent.NAVIGATE_LEFT.'''167 '''Handle UserEvent.NAVIGATE_LEFT.'''
200 if self.active_menu == "feed":168 if self.menu.active:
201 self.active_menu = "context"169 self._context_menu_activation()
202 self.li.hide_position()
203 self.menu.set_active(False)
204 self.description.hide()
205 self.context_menu.set_active(True)
206170
207 def _handle_right(self):171 def _handle_right(self):
208 '''Handle UserEvent.NAVIGATE_RIGHT.'''172 '''Handle UserEvent.NAVIGATE_RIGHT.'''
209 if self.active_menu == "context":173 if not self.menu.active:
210 self.active_menu = "feed"174 self._menu_activation()
211 self.li.show_position()
212 self.menu.set_active(True)
213 self.description.show()
214 self.context_menu.set_active(False)
215175
216 def _handle_select(self):176 def _handle_select(self, event=None):
217 '''Handle UserEvent.NAVIGATE_SELECT.'''177 '''Handle UserEvent.NAVIGATE_SELECT.'''
218 if self.active_menu == "context":178 if not self.menu.active:
219 index = self.context_menu.get_current_position()179 index = self.context_menu.selected_index
220 if index == 0:180 if index == 0:
221 # Send message to bus and update screen181 # Send message to bus and update screen
222 self.library.request_feed_update()182 self.library.request_feed_update()
@@ -235,14 +195,34 @@
235 self.update()195 self.update()
236 else:196 else:
237 # Feed selected from the menu. Change screen.197 # Feed selected from the menu. Change screen.
238 feed = Feed(self.menu.get_current_menuitem().get_userdata())198 feed = self.menu.selected_userdata
239 kwargs = { 'feed' : feed }199 kwargs = { 'feed' : feed }
240 self.callback("feed", kwargs)200 self.callback("feed", kwargs)
241201
242 def handle_user_event(self, event):202 def handle_user_event(self, event=None):
243 '''Handle screen specific user events unless the library is empty.'''203 '''Handle screen specific user events unless the library is empty.'''
244 if self.library.is_empty() or len(self.library.get_feeds()) == 0:204 if self.library.is_empty() or len(self.library.get_feeds()) == 0:
245 return205 return
246 else:206 else:
247 Screen.handle_user_event(self, event)207 Screen.handle_user_event(self, event)
248208
209 def _display_selected_feed(self, actor=None):
210 '''Update of the list indicator and the description label'''
211 self.li.set_current(self.menu.selected_index + 1)
212 feed = self.menu.selected_userdata
213 self.description.set_text(self.parser.strip_tags(feed.description))
214
215 def _menu_activation(self, actor=None):
216 '''Handle the menu activation'''
217 self.menu.active = True
218 self.context_menu.active = False
219 self.li.show_position()
220 self.description.show()
221
222 def _context_menu_activation(self, actor=None):
223 '''Handle the context menu activation'''
224 self.menu.active = False
225 self.context_menu.active = True
226 self.li.hide_position()
227 self.description.hide()
228
249229
=== modified file 'entertainerlib/frontend/gui/screens/screen.py'
--- entertainerlib/frontend/gui/screens/screen.py 2009-05-16 16:40:48 +0000
+++ entertainerlib/frontend/gui/screens/screen.py 2009-05-28 20:15:47 +0000
@@ -112,7 +112,7 @@
112 '''Handle UserEvent.NAVIGATE_RIGHT.'''112 '''Handle UserEvent.NAVIGATE_RIGHT.'''
113 pass113 pass
114114
115 def _handle_select(self):115 def _handle_select(self, event=None):
116 '''Handle UserEvent.NAVIGATE_SELECT.'''116 '''Handle UserEvent.NAVIGATE_SELECT.'''
117 pass117 pass
118118
@@ -134,6 +134,7 @@
134 if not self.has_tabs:134 if not self.has_tabs:
135 raise ScreenException('This screen does not support tabs.')135 raise ScreenException('This screen does not support tabs.')
136136
137 tab.tab_group = self.tab_group
137 self.tab_group.add_tab(tab)138 self.tab_group.add_tab(tab)
138 # XXX: laymansterms - figure this out, why do we need to add directly139 # XXX: laymansterms - figure this out, why do we need to add directly
139 # to the screen instead of the tab group, why is the positioning140 # to the screen instead of the tab group, why is the positioning
140141
=== modified file 'entertainerlib/frontend/gui/screens/tv_episodes.py'
--- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-01 11:15:57 +0000
+++ entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-12 07:57:05 +0000
@@ -5,14 +5,12 @@
5import pango5import pango
66
7from entertainerlib.frontend.gui.screens.screen import Screen7from entertainerlib.frontend.gui.screens.screen import Screen
8from entertainerlib.frontend.gui.user_event import UserEvent
9from entertainerlib.frontend.gui.widgets.eyecandy_texture import (8from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
10 EyeCandyTexture)9 EyeCandyTexture)
11from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.frontend.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator11from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.text_menu import TextMenu12from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
14from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea13from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
15from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1614
17class TvEpisodes(Screen):15class TvEpisodes(Screen):
18 '''Screen contains list of all episodes of one specific season.'''16 '''Screen contains list of all episodes of one specific season.'''
@@ -32,58 +30,42 @@
32 self.add(screen_title)30 self.add(screen_title)
3331
34 self.scroll_area = None32 self.scroll_area = None
35 self.episode_menu = None33 self.menu = None
36 self.title = None34 self.title = None
37 self.thumb = None35 self.thumb = None
38 self._create_episode_menu()36 self.menu = self._create_episode_menu()
37 self.add(self.menu)
39 self._create_episode_info_box()38 self._create_episode_info_box()
4039
41 #List indicator40 #List indicator
42 self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)41 self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
43 self.li.set_maximum(self.episode_menu.get_number_of_items())42 self.li.set_maximum(self.menu.count)
44 self.add(self.li)43 self.add(self.li)
4544
46 # Add the additional actions that are needed but not handled by default45 self.menu.connect("moved", self._update_episode_info)
47 self.event_handlers.update({46 self.menu.connect("selected", self._handle_select)
48 UserEvent.NAVIGATE_FIRST_PAGE : self._handle_first_page,
49 UserEvent.NAVIGATE_LAST_PAGE : self._handle_last_page,
50 UserEvent.NAVIGATE_PREVIOUS_PAGE : self._handle_previous_page,
51 UserEvent.NAVIGATE_NEXT_PAGE : self._handle_next_page
52 })
5347
54 def _create_episode_menu(self):48 def _create_episode_menu(self):
55 """49 """Create a list of available seasons."""
56 Create a list of available seasons50 menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781)
57 """
58 self.episode_menu = TextMenu(self.theme, self.config.show_effects())
59 self.episode_menu.set_row_count(1)
60 #self.episode_menu.set_visible_column_count(10)
61 self.episode_menu.set_item_size(self.get_abs_x(0.4393),
62 self.get_abs_y(0.0781))
63 self.episode_menu.set_position(self.get_abs_x(0.4978),
64 self.get_abs_y(0.1563))
6551
66 # Create menu items based on MusicLibrary
67 episodes = self.tv_series.get_episodes_from_season(self.season_number)52 episodes = self.tv_series.get_episodes_from_season(self.season_number)
68 for episode in episodes:53 episodes_list = [[_("%(num)d. %(title)s") % \
69 item = TextMenuItem(0.4393, 0.0781,54 {'num': episode.get_episode_number(), 'title': episode.get_title()},
70 str(episode.get_episode_number()) + ". " + episode.get_title())55 None, episode] for episode in episodes]
71 item.set_userdata(episode)56 menu.async_add(episodes_list)
72 self.episode_menu.add_actor(item)57
7358 menu.active = True
74 self.episode_menu.set_active(True)59
75 self.add(self.episode_menu)60 return menu
7661
77 def _create_thumbnail_texture(self):62 def _create_thumbnail_texture(self):
78 """63 """Create a thumbnail texture. This is called as menu is scrolled."""
79 Create a thumbnail texture. This is called as menu is scrolled
80 """
81 if self.thumb:64 if self.thumb:
82 self.thumb.hide()65 self.thumb.hide()
8366
84 # Thumbnail. Use cover art if thumbnail doesn't exist67 # Thumbnail. Use cover art if thumbnail doesn't exist
85 menu_item = self.episode_menu.get_current_menuitem()68 thumbnail = self.menu.selected_userdata.get_thumbnail_url()
86 thumbnail = menu_item.get_userdata().get_thumbnail_url()
87 if(thumbnail is not None):69 if(thumbnail is not None):
88 pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail)70 pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail)
89 thumb_width = 0.292871 thumb_width = 0.2928
@@ -115,7 +97,7 @@
11597
116 # Title98 # Title
117 self.title = Label(0.04, "title", 0.05, 0.55,99 self.title = Label(0.04, "title", 0.05, 0.55,
118 self.episode_menu.get_current_menuitem().get_userdata().get_title(),100 self.menu.selected_userdata.get_title(),
119 font_weight="bold")101 font_weight="bold")
120 self.title.set_ellipsize(pango.ELLIPSIZE_END)102 self.title.set_ellipsize(pango.ELLIPSIZE_END)
121 self.title.width = 0.4103 self.title.width = 0.4
@@ -123,79 +105,53 @@
123105
124 # Plot106 # Plot
125 plot = Label(0.029, "subtitle", 0, 0,107 plot = Label(0.029, "subtitle", 0, 0,
126 self.episode_menu.get_current_menuitem().get_userdata().get_plot())108 self.menu.selected_userdata.get_plot())
127 plot.width = 0.4109 plot.width = 0.4
128110
129 self.scroll_area = ScrollArea(0.4, 0.3516, 0.05, 0.63, plot)111 self.scroll_area = ScrollArea(0.4, 0.15, 0.05, 0.63, plot)
130 self.add(self.scroll_area)112 self.add(self.scroll_area)
131113
132 def _update_episode_info_box(self, episode):114 def _update_episode_info(self, event=None):
133 """115 '''Update information from this episode.'''
134 Update episode infobox116 self.li.set_current(self.menu.selected_index + 1)
135 @param episode: Display information from this episode117
136 """
137 self._create_thumbnail_texture()118 self._create_thumbnail_texture()
138 self.title.set_text(episode.get_title())119 self.title.set_text(self.menu.selected_userdata.get_title())
139 self.title.width = 0.4120 self.title.width = 0.4
140121
141 plot = Label(0.029, "subtitle", 0, 0,122 plot = Label(0.029, "subtitle", 0, 0,
142 self.episode_menu.get_current_menuitem().get_userdata().get_plot())123 self.menu.selected_userdata.get_plot())
143 plot.width = 0.4124 plot.width = 0.4
144 self.scroll_area.set_content(plot)125 self.scroll_area.set_content(plot)
145126
146 def _move_menu(self, menu_direction):
147 '''Move the menu in the given direction.'''
148 if menu_direction == TextMenu.UP:
149 scroll_direction = self.scroll_area.scroll_up
150 elif menu_direction == TextMenu.DOWN:
151 scroll_direction = self.scroll_area.scroll_down
152
153 if self.episode_menu.is_active():
154 self.episode_menu.move(menu_direction)
155 self.li.set_current(self.episode_menu.get_current_position() + 1)
156 self._update_episode_info_box(
157 self.episode_menu.get_current_menuitem().get_userdata())
158 else:
159 scroll_direction()
160
161 def _handle_up(self):127 def _handle_up(self):
162 '''Handle UserEvent.NAVIGATE_UP.'''128 '''Handle UserEvent.NAVIGATE_UP.'''
163 self._move_menu(TextMenu.UP)129 if self.menu.active:
130 self.menu.up()
131 else:
132 self.scroll_area.scroll_up()
164133
165 def _handle_down(self):134 def _handle_down(self):
166 '''Handle UserEvent.NAVIGATE_DOWN.'''135 '''Handle UserEvent.NAVIGATE_DOWN.'''
167 self._move_menu(TextMenu.DOWN)136 if self.menu.active:
137 self.menu.down()
138 else:
139 self.scroll_area.scroll_down()
168140
169 def _handle_left(self):141 def _handle_left(self):
170 '''Handle UserEvent.NAVIGATE_LEFT.'''142 '''Handle UserEvent.NAVIGATE_LEFT.'''
171 self.episode_menu.set_active(False)143 self.menu.active = False
172 self.scroll_area.set_active(True)144 self.scroll_area.set_active(True)
173145
174 def _handle_right(self):146 def _handle_right(self):
175 '''Handle UserEvent.NAVIGATE_RIGHT.'''147 '''Handle UserEvent.NAVIGATE_RIGHT.'''
176 self.episode_menu.set_active(True)148 self.menu.active = True
177 self.scroll_area.set_active(False)149 self.scroll_area.set_active(False)
178150
179 def _handle_select(self):151 def _handle_select(self, event=None):
180 '''Handle UserEvent.NAVIGATE_SELECT.'''152 '''Handle UserEvent.NAVIGATE_SELECT.'''
181 episode = self.episode_menu.get_current_menuitem().get_userdata()153 episode = self.menu.selected_userdata
182 self.media_player.set_media(episode)154 self.media_player.set_media(episode)
183 self.media_player.play()155 self.media_player.play()
184 self.callback("video_osd")156 self.callback("video_osd")
185157
186 def _handle_first_page(self):
187 '''Handle UserEvent.NAVIGATE_FIRST_PAGE.'''
188 self.scroll_area.scroll_to_top()
189
190 def _handle_last_page(self):
191 '''Handle UserEvent.NAVIGATE_LAST_PAGE.'''
192 self.scroll_area.scroll_to_bottom()
193
194 def _handle_previous_page(self):
195 '''Handle UserEvent.NAVIGATE_PREVIOUS_PAGE.'''
196 self.scroll_area.scroll_page_up()
197
198 def _handle_next_page(self):
199 '''Handle UserEvent.NAVIGATE_NEXT_PAGE.'''
200 self.scroll_area.scroll_page_down()
201
202158
=== modified file 'entertainerlib/frontend/gui/screens/tv_series.py'
--- entertainerlib/frontend/gui/screens/tv_series.py 2009-07-01 11:15:57 +0000
+++ entertainerlib/frontend/gui/screens/tv_series.py 2009-07-12 07:57:05 +0000
@@ -9,7 +9,6 @@
9from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.frontend.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
12from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1312
14class TvSeries(Screen):13class TvSeries(Screen):
15 '''Screen that contains all seasons of one TV series.'''14 '''Screen that contains all seasons of one TV series.'''
@@ -26,37 +25,35 @@
26 self.add(screen_title)25 self.add(screen_title)
2726
28 self.art = None27 self.art = None
29 self.season_menu = None28 self.menu = None
30 self._create_series_cover_texture()29 self._create_series_cover_texture()
31 self._create_season_menu()30 self.menu = self._create_season_menu()
31 self.add(self.menu)
3232
33 #List indicator33 #List indicator
34 self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)34 self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
35 self.li.set_maximum(self.season_menu.get_number_of_items())35 self.li.set_maximum(len(self.tv_series.get_seasons()))
36 self.add(self.li)36 self.add(self.li)
3737
38 self.menu.connect("moved", self._update_season_info)
39 self.menu.connect("selected", self._handle_select)
40
38 def _create_season_menu(self):41 def _create_season_menu(self):
39 """42 """
40 Create a list of available seasons43 Create a list of available seasons
41 """44 """
42 self.season_menu = TextMenu(self.theme, self.config.show_effects())45 menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781)
43 self.season_menu.set_row_count(1)
44 self.season_menu.set_item_size(self.get_abs_x(0.4393),
45 self.get_abs_y(0.0781))
46 self.season_menu.set_position(self.get_abs_x(0.4978),
47 self.get_abs_y(0.1563))
4846
49 # Create menu items based on MusicLibrary
50 seasons = self.tv_series.get_seasons()47 seasons = self.tv_series.get_seasons()
51 seasons.sort()48 seasons.sort()
52 for season in seasons:49
53 item = TextMenuItem(0.4393, 0.0781,50 seasons_list = [[_("Season %(num)s") % {'num': season}, None, season] \
54 _("Season %(num)s") % {'num': season})51 for season in seasons]
55 item.set_userdata(season)52 menu.async_add(seasons_list)
56 self.season_menu.add_actor(item)53
5754 menu.active = True
58 self.season_menu.set_active(True)55
59 self.add(self.season_menu)56 return menu
6057
61 def _create_series_cover_texture(self):58 def _create_series_cover_texture(self):
62 """59 """
@@ -72,22 +69,21 @@
72 self.art = EyeCandyTexture(0.16, 0.15, 0.2196, 0.5859, pixbuf)69 self.art = EyeCandyTexture(0.16, 0.15, 0.2196, 0.5859, pixbuf)
73 self.add(self.art)70 self.add(self.art)
7471
75 def _move_menu(self, menu_direction):72 def _update_season_info(self, event=None):
76 '''Move the menu in the given direction.'''73 '''Update the ListIndicator.'''
77 self.season_menu.move(menu_direction)74 self.li.set_current(self.menu.selected_index + 1)
78 self.li.set_current(self.season_menu.get_current_position() + 1)
7975
80 def _handle_up(self):76 def _handle_up(self):
81 '''Handle UserEvent.NAVIGATE_UP.'''77 '''Handle UserEvent.NAVIGATE_UP.'''
82 self._move_menu(TextMenu.UP)78 self.menu.up()
8379
84 def _handle_down(self):80 def _handle_down(self):
85 '''Handle UserEvent.NAVIGATE_DOWN.'''81 '''Handle UserEvent.NAVIGATE_DOWN.'''
86 self._move_menu(TextMenu.DOWN)82 self.menu.down()
8783
88 def _handle_select(self):84 def _handle_select(self, event=None):
89 '''Handle UserEvent.NAVIGATE_SELECT.'''85 '''Handle UserEvent.NAVIGATE_SELECT.'''
90 season = self.season_menu.get_current_menuitem().get_userdata()86 season = self.menu.selected_userdata
91 kwargs = { 'tv_series' : self.tv_series, 'season_number' : season }87 kwargs = { 'tv_series' : self.tv_series, 'season_number' : season }
92 self.callback("tv_episodes", kwargs)88 self.callback("tv_episodes", kwargs)
9389
9490
=== modified file 'entertainerlib/frontend/gui/tabs/albums_tab.py'
--- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/albums_tab.py 2009-07-14 11:06:21 +0000
@@ -2,18 +2,14 @@
2'''Albums tab which displays albums and allows users to select them'''2'''Albums tab which displays albums and allows users to select them'''
3# pylint: disable-msg=W10013# pylint: disable-msg=W1001
44
5import clutter
6import gobject
7import pango5import pango
86
9from entertainerlib.frontend.gui.tabs.tab import Tab7from entertainerlib.frontend.gui.tabs.tab import Tab
10from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu8from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
11from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem
12from entertainerlib.frontend.gui.widgets.label import Label9from entertainerlib.frontend.gui.widgets.label import Label
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator10from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
14from entertainerlib.frontend.gui.widgets.loading_animation import (11from entertainerlib.frontend.gui.widgets.loading_animation import (
15 LoadingAnimation)12 LoadingAnimation)
16from entertainerlib.frontend.gui.widgets.texture import Texture
1713
18class AlbumsTab(Tab):14class AlbumsTab(Tab):
19 '''Tab to show album listings'''15 '''Tab to show album listings'''
@@ -28,48 +24,28 @@
28 self.add(self.throbber)24 self.add(self.throbber)
2925
30 if len(albums) < 4:26 if len(albums) < 4:
31 # XXX: laymansterms - This is "less than elegant." I'm not fixing
32 # ImageMenu right now so we can't get rid of tex_size_x and y yet.
33 # We really need to eliminate the concept tex_size_y, but it will
34 # have to wait for now.
35 x_percent = 0.292827 x_percent = 0.2928
36 tex_size_x = self.get_abs_x(x_percent)28 visible_rows = 1
37 tex_size_y = self.get_abs_y(0.5208)29 visible_cols = 3
38 row_count = 1
39 visible = 3
40 elif len(albums) < 13:30 elif len(albums) < 13:
41 x_percent = 0.146431 x_percent = 0.1464
42 tex_size_x = self.get_abs_x(x_percent)32 visible_rows = 2
43 tex_size_y = self.get_abs_y(0.2604)33 visible_cols = 6
44 row_count = 2
45 visible = 6
46 else:34 else:
47 x_percent = 0.109835 x_percent = 0.1098
48 tex_size_x = self.get_abs_x(x_percent)36 visible_rows = 3
49 tex_size_y = self.get_abs_y(0.1953)37 visible_cols = 8
50 row_count = 3
51 visible = 8
5238
53 # Create albums menu39 # Create albums menu
54 self.menu = ImageMenu(self.theme)40 self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent))
55 self.menu.set_row_count(row_count)41 self.menu.visible_rows = visible_rows
56 self.menu.set_visible_column_count(visible)42 self.menu.visible_cols = visible_cols
57 self.menu.set_item_size(tex_size_x, tex_size_y)43 self.menu.items_per_col = self.menu.visible_rows
58
59 # Default texture
60 self.default_cover = Texture(self.theme.getImage("default_album_art"))
61
62 self._async_menu_build(albums, x_percent)
63 self._selected_album = self.menu.get_current_menuitem().get_userdata()
64
65 self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.1823))
66 self.menu.show()
67
68 if self.config.show_effects():
69 self.menu.set_animate(True)
70
71 self.add(self.menu)44 self.add(self.menu)
7245
46 albums_list = [[album.get_album_art_url(), album] for album in albums]
47 self.menu.async_add_albums(albums_list)
48
73 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,49 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
74 ListIndicator.HORIZONTAL)50 ListIndicator.HORIZONTAL)
75 self.li.set_maximum(len(albums))51 self.li.set_maximum(len(albums))
@@ -92,102 +68,79 @@
9268
93 self.connect('activated', self._on_activated)69 self.connect('activated', self._on_activated)
94 self.connect('deactivated', self._on_deactivated)70 self.connect('deactivated', self._on_deactivated)
9571 self.menu.connect("moved", self._update_album_info)
96 def _async_menu_build(self, albums, item_size_percent):72 self.menu.connect("selected", self._handle_select)
97 """73 self.menu.connect("activated", self._on_activated)
98 This function is called as an async callback. It is also a recursive74 self.menu.connect("filled", self._on_menu_filled)
99 function. This allows us to load menu items in the background without
100 blocking the user interface.
101 """
102
103 # Check if we should stop recursive calls (menu is complete)
104 if len(albums) > 0:
105 album = albums[0]
106 if album.has_album_art():
107 texture = Texture(album.get_album_art_url())
108 else:
109 texture = clutter.CloneTexture(self.default_cover)
110
111 item = ImageMenuItem(item_size_percent, 1, texture)
112 item.set_userdata(album)
113 self.menu.add_actor(item)
114 # Recursive call, remove first album from the list
115 gobject.timeout_add(25, self._async_menu_build, albums[1:],
116 item_size_percent)
117 return False
118 else:
119 self.throbber.hide()
120 # Done loading the albums, notify gobject to stop calling menu build
121 return False
122
123 def _get_selected_album(self):
124 '''Get the currently selected album from the menu.'''
125 return self.menu.get_current_menuitem().get_userdata()
126
127 selected_album = property(_get_selected_album)
12875
129 def can_activate(self):76 def can_activate(self):
130 '''Albums tab will always be created from an existing artist with at77 '''Albums tab will always be created from an existing artist with at
131 least one album.'''78 least one album.'''
132 return True79 return True
13380
134 def _update_album_info(self):81 def _update_album_info(self, event=None):
135 '''Update the album information labels.'''82 '''Update the album information labels.'''
136 if self.active:83 if self.active:
137 album = self.menu.get_current_menuitem().get_userdata()84 album = self.menu.selected_userdata
138 self.album_title.set_text(album.get_title())85 self.album_title.set_text(album.get_title())
139 self.album_artist.set_text(album.get_artist())86 self.album_artist.set_text(album.get_artist())
140 self.album_tracks.set_text(_("%(total)s tracks") % \87 self.album_tracks.set_text(_("%(total)s tracks") % \
141 {'total': album.get_number_of_tracks()})88 {'total': album.get_number_of_tracks()})
89 self.li.show()
90 self.li.set_current(self.menu.selected_index + 1)
142 else:91 else:
143 self.album_title.set_text("")92 self.album_title.set_text("")
144 self.album_artist.set_text("")93 self.album_artist.set_text("")
145 self.album_tracks.set_text("")94 self.album_tracks.set_text("")
14695 self.li.hide()
147 def _update_menu(self, direction):
148 '''Update the menu based on the provided menu direction.'''
149 self.menu.move(direction)
150 self._update_album_info()
151 self.li.set_current(self.menu.get_current_position() + 1)
152 return False
15396
154 def _handle_up(self):97 def _handle_up(self):
155 '''Handle the up user event.'''98 '''Handle the up user event.'''
156 if self.menu.get_cursor_position()[1] == 0:99 if self.menu.on_top:
157 return True # Move control back to tab bar100 return True # Move control back to tab bar
158 else:101 else:
159 return self._update_menu(self.menu.UP)102 self.menu.up()
103 return False
160104
161 def _handle_down(self):105 def _handle_down(self):
162 '''Handle the down user event.'''106 '''Handle the down user event.'''
163 return self._update_menu(self.menu.DOWN)107 self.menu.down()
108 return False
164109
165 def _handle_left(self):110 def _handle_left(self):
166 '''Handle the left user event.'''111 '''Handle the left user event.'''
167 return self._update_menu(self.menu.LEFT)112 self.menu.left()
113 return False
168114
169 def _handle_right(self):115 def _handle_right(self):
170 '''Handle the right user event.'''116 '''Handle the right user event.'''
171 return self._update_menu(self.menu.RIGHT)117 self.menu.right()
118 return False
172119
173 def _handle_select(self):120 def _handle_select(self, event=None):
174 '''Handle the select user event.'''121 '''Handle the select user event.'''
175 album = self.menu.get_current_menuitem().get_userdata()122 album = self.menu.selected_userdata
176 kwargs = { 'album' : album }123 kwargs = { 'album' : album }
177 self.callback("album", kwargs)124 self.callback("album", kwargs)
178 return False125 return False
179126
180 def _on_activated(self, event=None):127 def _on_activated(self, event=None):
181 '''Tab activated.'''128 '''Tab activated.'''
129 if self.tab_group is not None:
130 self.tab_group.active = False
131 self.menu.active = True
132 self.active = True
182 self._update_album_info()133 self._update_album_info()
183 self.menu.set_active(True)
184 self.menu.set_opacity(255)
185 return False134 return False
186135
187 def _on_deactivated(self, event=None):136 def _on_deactivated(self, event=None):
188 '''Tab deactivated.'''137 '''Tab deactivated.'''
138 self.active = False
139 self.menu.active = False
189 self._update_album_info()140 self._update_album_info()
190 self.menu.set_active(False)
191 self.menu.set_opacity(128)
192 return False141 return False
193142
143 def _on_menu_filled(self, event=None):
144 '''Handles filled event.'''
145 self.throbber.hide()
146
194147
=== modified file 'entertainerlib/frontend/gui/tabs/artists_tab.py'
--- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/artists_tab.py 2009-07-14 11:06:21 +0000
@@ -2,8 +2,6 @@
2'''Artists tab for the music screen'''2'''Artists tab for the music screen'''
3# pylint: disable-msg=W10013# pylint: disable-msg=W1001
44
5import clutter
6import gobject
7import pango5import pango
86
9from entertainerlib.frontend.gui.tabs.tab import Tab7from entertainerlib.frontend.gui.tabs.tab import Tab
@@ -12,7 +10,6 @@
12from entertainerlib.frontend.gui.widgets.loading_animation import (10from entertainerlib.frontend.gui.widgets.loading_animation import (
13 LoadingAnimation)11 LoadingAnimation)
14from entertainerlib.frontend.gui.widgets.text_menu import TextMenu12from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
15from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1613
17class ArtistsTab(Tab):14class ArtistsTab(Tab):
18 '''Tab for the music screen to show artist listings'''15 '''Tab for the music screen to show artist listings'''
@@ -27,19 +24,18 @@
27 self.throbber.show()24 self.throbber.show()
28 self.add(self.throbber)25 self.add(self.throbber)
2926
30 self.menu = TextMenu(self.config.theme, self.config.show_effects())27 self.menu = TextMenu(0.057, 0.208, 0.293, 0.078)
31 self.menu.set_item_size(self.get_abs_x(0.293), self.get_abs_y(0.078))28 self.menu.items_per_row = 3
32 cursor = clutter.Rectangle()29 self.menu.visible_rows = 7
33 cursor.set_color((0, 0, 0, 0))30 self.menu.visible_cols = 3
34 self.menu.set_cursor(cursor)31 self.menu.active = False
35 self.menu.set_orientation(TextMenu.HORIZONTAL)32 self.menu.cursor = None
36 self.menu.set_row_count(7)33 self.menu.items_anchor_west = False
37 self.menu.set_visible_column_count(3)
38 self.menu.set_position(self.get_abs_x(0.057), self.get_abs_y(0.208))
39 if self.config.show_effects():
40 self.menu.set_animate(True)
41 self.add(self.menu)34 self.add(self.menu)
4235
36 artists_list = [[artist, None, artist] for artist in artists]
37 self.menu.async_add_artists(artists_list)
38
43 # Create artist label39 # Create artist label
44 self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")40 self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")
45 self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)41 self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)
@@ -54,107 +50,85 @@
5450
55 # Create artis menu list indicator51 # Create artis menu list indicator
56 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,52 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
57 ListIndicator.HORIZONTAL)53 ListIndicator.VERTICAL)
58 self.li.set_maximum(len(artists))54 self.li.set_maximum(len(artists))
59 self.add(self.li)55 self.add(self.li)
6056
61 self._async_menu_build(artists)
62 self._selected_artist = self.menu.get_current_menuitem().get_userdata()
63
64 self.connect('activated', self._on_activated)57 self.connect('activated', self._on_activated)
65 self.connect('deactivated', self._on_deactivated)58 self.connect('deactivated', self._on_deactivated)
6659 self.menu.connect("moved", self._update_artist_info)
67 def _async_menu_build(self, artists):60 self.menu.connect("selected", self._handle_select)
68 """61 self.menu.connect("activated", self._on_activated)
69 This function is called as an async callback. It is also a recursive62 self.menu.connect("filled", self._on_menu_filled)
70 function. This allows us to load menu items in the background without
71 blocking the user interface.
72 """
73
74 # Check if we should stop recursive calls (menu is complete)
75 if len(artists) > 0:
76 artist = artists[0]
77 item = TextMenuItem(0.293, 0.104, artist)
78 item.justified = True
79 item.set_userdata(artist)
80 self.menu.add_actor(item)
81 # Recursive call, remove first artist from the list
82 gobject.timeout_add(15, self._async_menu_build, artists[1:])
83 return False
84 else:
85 self.throbber.hide()
86 # Done loading artists, notify gobject to stop calling menu build
87 return False
88
89 def _get_selected_artist(self):
90 '''Get the currently selected artist from the menu.'''
91 return self.menu.get_current_menuitem().get_userdata()
92
93 selected_artist = property(_get_selected_artist)
9463
95 def can_activate(self):64 def can_activate(self):
96 '''Albums tab will always be created from an existing artist with at65 '''Albums tab will always be created from an existing artist with at
97 least one album.'''66 least one album.'''
98 return True67 return True
9968
100 def _update_artist_info(self):69 def _update_artist_info(self, event=None):
101 '''Update the artist information labels'''70 '''Update the artist information labels'''
102 if self.active:71 if self.active:
103 artist = self.menu.get_current_menuitem().get_userdata()72 artist = self.menu.selected_userdata
104 self.artist_title.set_text(artist)73 self.artist_title.set_text(artist)
105 self.artist_albums.set_text(_("%(albums)d albums") %74 self.artist_albums.set_text(_("%(albums)d albums") %
106 {'albums': self.library.number_of_albums_by_artist(artist)})75 {'albums': self.library.number_of_albums_by_artist(artist)})
107 self.artist_tracks.set_text(_("%(tracks)d tracks") %76 self.artist_tracks.set_text(_("%(tracks)d tracks") %
108 {'tracks': self.library.number_of_tracks_by_artist(artist)})77 {'tracks': self.library.number_of_tracks_by_artist(artist)})
78 self.li.show()
79 self.li.set_current(self.menu.selected_index + 1)
109 else:80 else:
110 self.artist_title.set_text("")81 self.artist_title.set_text("")
111 self.artist_albums.set_text("")82 self.artist_albums.set_text("")
112 self.artist_tracks.set_text("")83 self.artist_tracks.set_text("")
11384 self.li.hide()
114 def _update_menu(self, direction):
115 '''Update the menu based on the provided menu direction.'''
116 self.menu.move(direction)
117 self._update_artist_info()
118 self.li.set_current(self.menu.get_current_position() + 1)
119 return False
12085
121 def _handle_up(self):86 def _handle_up(self):
122 '''Handle the up user event.'''87 '''Handle the up user event.'''
123 if self.menu.get_cursor_position()[1] == 0:88 if self.menu.on_top:
124 return True # Move control back to tab bar89 return True # Move control back to tab bar
125 else:90 else:
126 return self._update_menu(self.menu.UP)91 self.menu.up()
92 return False
12793
128 def _handle_down(self):94 def _handle_down(self):
129 '''Handle the down user event.'''95 '''Handle the down user event.'''
130 return self._update_menu(self.menu.DOWN)96 self.menu.down()
97 return False
13198
132 def _handle_left(self):99 def _handle_left(self):
133 '''Handle the left user event.'''100 '''Handle the left user event.'''
134 return self._update_menu(self.menu.LEFT)101 self.menu.left()
102 return False
135103
136 def _handle_right(self):104 def _handle_right(self):
137 '''Handle the right user event.'''105 '''Handle the right user event.'''
138 return self._update_menu(self.menu.RIGHT)106 self.menu.right()
107 return False
139108
140 def _handle_select(self):109 def _handle_select(self, event=None):
141 '''Handle the select user event.'''110 '''Handle the select user event.'''
142 artist = self.menu.get_current_menuitem().get_userdata()111 artist = self.menu.selected_userdata
143 kwargs = { 'artist' : artist }112 kwargs = { 'artist' : artist }
144 self.callback("artist", kwargs)113 self.callback("artist", kwargs)
145 return False114 return False
146115
147 def _on_activated(self, event=None):116 def _on_activated(self, event=None):
148 '''Tab activated.'''117 '''Tab activated.'''
118 if self.tab_group is not None:
119 self.tab_group.active = False
120 self.menu.active = True
121 self.active = True
149 self._update_artist_info()122 self._update_artist_info()
150 self.menu.set_active(True)
151 self.menu.set_opacity(255)
152 return False123 return False
153124
154 def _on_deactivated(self, event=None):125 def _on_deactivated(self, event=None):
155 '''Tab deactivated.'''126 '''Tab deactivated.'''
127 self.active = False
128 self.menu.active = False
156 self._update_artist_info()129 self._update_artist_info()
157 self.menu.set_active(False)130
158 self.menu.set_opacity(128)131 def _on_menu_filled(self, event=None):
159 return False132 '''Handles filled event.'''
133 self.throbber.hide()
160134
161135
=== modified file 'entertainerlib/frontend/gui/tabs/movies_tab.py'
--- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/movies_tab.py 2009-07-14 11:06:21 +0000
@@ -1,17 +1,14 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''MoviesTab - MoviesTab is group of objects that are displayed on movie tab'''2'''MoviesTab - MoviesTab is group of objects that are displayed on movie tab'''
33
4import clutter
5import gtk
6import pango4import pango
75
8from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.frontend.gui.tabs.tab import Tab
9from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
10from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem
11from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.frontend.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture10from entertainerlib.frontend.gui.widgets.loading_animation import (
14from entertainerlib.frontend.gui.widgets.texture import Texture11 LoadingAnimation)
1512
16class MoviesTab(Tab):13class MoviesTab(Tab):
17 """14 """
@@ -24,6 +21,7 @@
24 def __init__(self, video_library, move_to_new_screen_callback,21 def __init__(self, video_library, move_to_new_screen_callback,
25 name="movies", tab_title=_("Movies")):22 name="movies", tab_title=_("Movies")):
26 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)23 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
24
27 self.video_library = video_library25 self.video_library = video_library
28 self.theme = self.config.theme26 self.theme = self.config.theme
29 self.list_indicator = None27 self.list_indicator = None
@@ -35,7 +33,17 @@
35 if self.video_library.get_number_of_movies() == 0:33 if self.video_library.get_number_of_movies() == 0:
36 self._create_empty_library_notice()34 self._create_empty_library_notice()
37 else:35 else:
38 self._create_menu()36 # Start the loading animation while the menu is loading
37 self.throbber = LoadingAnimation(0.1, 0.1)
38 self.throbber.show()
39 self.add(self.throbber)
40
41 self.menu = self._create_menu()
42 self.add(self.menu)
43 self.menu.connect("moved", self._update_movie_info)
44 self.menu.connect("selected", self._handle_select)
45 self.menu.connect("activated", self._on_activated)
46 self.menu.connect("filled", self._on_menu_filled)
3947
40 self.connect('activated', self._on_activated)48 self.connect('activated', self._on_activated)
41 self.connect('deactivated', self._on_deactivated)49 self.connect('deactivated', self._on_deactivated)
@@ -67,40 +75,19 @@
67 the video library.75 the video library.
68 """76 """
69 #Create movie menu77 #Create movie menu
70 self.menu = ImageMenu(self.theme)78 menu = ImageMenu(0.06, 0.18, 0.12, 0.25)
71 self.menu.set_item_size(self.get_abs_x(0.1), self.get_abs_y(0.25))79 menu.items_per_col = 2
72 self.menu.set_item_spacing(self.get_abs_y(0.025))80 menu.visible_rows = 2
73 self.menu.set_visible_column_count(2)81 menu.visible_cols = 7
74 self.menu.set_row_count(7)
75 self.menu.set_orientation(self.menu.VERTICAL)
76
77 # Default texture
78 default_cover = Texture(self.theme.getImage("default_movie_art"))
7982
80 movies = self.video_library.get_movies()83 movies = self.video_library.get_movies()
81 for movie in movies:84 movies_list = [[movie.get_cover_art_url(), movie] for movie in movies]
82 if movie.has_cover_art():85 menu.async_add_videos(movies_list)
83 pix_buffer = gtk.gdk.pixbuf_new_from_file(
84 movie.get_cover_art_url())
85 texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
86 else:
87 texture = clutter.CloneTexture(default_cover)
88 item = ImageMenuItem(0.1, 1.41, texture)
89 item.set_userdata(movie)
90 self.menu.add_actor(item)
91
92 self.menu.set_position(self.get_abs_x(0.11), self.get_abs_y(0.2))
93 self.menu.show()
94
95 if self.config.show_effects():
96 self.menu.set_animate(True)
97
98 self.add(self.menu)
9986
100 # Create list indicator87 # Create list indicator
101 self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,88 self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,
102 ListIndicator.VERTICAL)89 ListIndicator.HORIZONTAL)
103 self.list_indicator.set_maximum(self.menu.get_number_of_items())90 self.list_indicator.set_maximum(len(movies))
104 self.show()91 self.show()
105 self.add(self.list_indicator)92 self.add(self.list_indicator)
10693
@@ -120,10 +107,12 @@
120 self.movie_plot.width = 0.7107 self.movie_plot.width = 0.7
121 self.add(self.movie_plot)108 self.add(self.movie_plot)
122109
123 def _update_movie_info(self):110 return menu
111
112 def _update_movie_info(self, event=None):
124 '''Update the movie information labels.'''113 '''Update the movie information labels.'''
125 if self.active:114 if self.active:
126 movie = self.menu.get_current_menuitem().get_userdata()115 movie = self.menu.selected_userdata
127 genres = movie.get_genres()116 genres = movie.get_genres()
128 if len(genres) > 1:117 if len(genres) > 1:
129 genre = genres[0] + "/" + genres[1]118 genre = genres[0] + "/" + genres[1]
@@ -135,55 +124,61 @@
135 self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \124 self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \
136 {'min': movie.get_runtime(), 'genre': genre})125 {'min': movie.get_runtime(), 'genre': genre})
137 self.movie_plot.set_text(movie.get_short_plot())126 self.movie_plot.set_text(movie.get_short_plot())
127 self.list_indicator.show()
128 self.list_indicator.set_current(self.menu.selected_index + 1)
138 else:129 else:
139 self.movie_title.set_text("")130 self.movie_title.set_text("")
140 self.movie_info.set_text("")131 self.movie_info.set_text("")
141 self.movie_plot.set_text("")132 self.movie_plot.set_text("")
142133 self.list_indicator.hide()
143 def _update_menu(self, direction):
144 '''Update the menu based on the provided menu direction.'''
145 self.menu.move(direction)
146 self._update_movie_info()
147 self.list_indicator.set_current(self.menu.get_current_position() + 1)
148 return False
149134
150 def _handle_up(self):135 def _handle_up(self):
151 '''Handle the up user event.'''136 '''Handle the up user event.'''
152 if self.menu.get_current_position() <= 6:137 if self.menu.on_top:
153 return True # Move control back to tab bar138 return True # Move control back to tab bar
154 else:139 else:
155 return self._update_menu(self.menu.UP)140 self.menu.up()
141 return False
156142
157 def _handle_down(self):143 def _handle_down(self):
158 '''Handle the down user event.'''144 '''Handle the down user event.'''
159 return self._update_menu(self.menu.DOWN)145 self.menu.down()
146 return False
160147
161 def _handle_left(self):148 def _handle_left(self):
162 '''Handle the left user event.'''149 '''Handle the left user event.'''
163 return self._update_menu(self.menu.LEFT)150 self.menu.left()
151 return False
164152
165 def _handle_right(self):153 def _handle_right(self):
166 '''Handle the right user event.'''154 '''Handle the right user event.'''
167 return self._update_menu(self.menu.RIGHT)155 self.menu.right()
156 return False
168157
169 def _handle_select(self):158 def _handle_select(self, event=None):
170 '''Handle the select user event.'''159 '''Handle the select user event.'''
171 movie = self.menu.get_current_menuitem().get_userdata()160 movie = self.menu.selected_userdata
172 kwargs = { 'movie' : movie }161 kwargs = { 'movie' : movie }
173 self.callback("movie", kwargs)162 self.callback("movie", kwargs)
174 return False163 return False
175164
176 def _on_activated(self, event=None):165 def _on_activated(self, event=None):
177 '''Tab activated.'''166 '''Tab activated.'''
167 if self.tab_group is not None:
168 self.tab_group.active = False
169 self.menu.active = True
170 self.active = True
178 self._update_movie_info()171 self._update_movie_info()
179 self.menu.set_active(True)
180 self.menu.set_opacity(255)
181 return False172 return False
182173
183 def _on_deactivated(self, event=None):174 def _on_deactivated(self, event=None):
184 '''Tab deactivated.'''175 '''Tab deactivated.'''
176 self.active = False
177 self.menu.active = False
185 self._update_movie_info()178 self._update_movie_info()
186 self.menu.set_active(False)
187 self.menu.set_opacity(128)
188 return False179 return False
189180
181 def _on_menu_filled(self, event=None):
182 '''Handles filled event.'''
183 self.throbber.hide()
184
190185
=== modified file 'entertainerlib/frontend/gui/tabs/series_tab.py'
--- entertainerlib/frontend/gui/tabs/series_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/series_tab.py 2009-07-14 11:06:21 +0000
@@ -1,17 +1,14 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''SeriesTab - Group of objects that are displayed on tv-series tab'''2'''SeriesTab - Group of objects that are displayed on tv-series tab'''
33
4import clutter
5import gtk
6import pango4import pango
75
8from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.frontend.gui.tabs.tab import Tab
9from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu7from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
10from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem
11from entertainerlib.frontend.gui.widgets.label import Label8from entertainerlib.frontend.gui.widgets.label import Label
12from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
13from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture10from entertainerlib.frontend.gui.widgets.loading_animation import (
14from entertainerlib.frontend.gui.widgets.texture import Texture11 LoadingAnimation)
1512
16class SeriesTab(Tab):13class SeriesTab(Tab):
17 """14 """
@@ -24,6 +21,7 @@
24 def __init__(self, video_library, move_to_new_screen_callback,21 def __init__(self, video_library, move_to_new_screen_callback,
25 name="series", tab_title=_("TV-Series")):22 name="series", tab_title=_("TV-Series")):
26 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)23 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
24
27 self.video_library = video_library25 self.video_library = video_library
28 self.theme = self.config.theme26 self.theme = self.config.theme
29 self.list_indicator = None27 self.list_indicator = None
@@ -34,7 +32,17 @@
34 if self.video_library.get_number_of_tv_series() == 0:32 if self.video_library.get_number_of_tv_series() == 0:
35 self._create_empty_library_notice()33 self._create_empty_library_notice()
36 else:34 else:
37 self._create_menu()35 # Start the loading animation while the menu is loading
36 self.throbber = LoadingAnimation(0.4, 0.1)
37 self.throbber.show()
38 self.add(self.throbber)
39
40 self.menu = self._create_menu()
41 self.add(self.menu)
42 self.menu.connect("moved", self._update_serie_info)
43 self.menu.connect("selected", self._handle_select)
44 self.menu.connect("activated", self._on_activated)
45 self.menu.connect("filled", self._on_menu_filled)
3846
39 self.connect('activated', self._on_activated)47 self.connect('activated', self._on_activated)
40 self.connect('deactivated', self._on_deactivated)48 self.connect('deactivated', self._on_deactivated)
@@ -66,42 +74,19 @@
66 the video library.74 the video library.
67 """75 """
68 #Create TV-series menu76 #Create TV-series menu
69 self.menu = ImageMenu(self.theme)77 menu = ImageMenu(0.06, 0.18, 0.12, 0.25)
70 self.menu.set_item_size(self.get_abs_x(0.1), self.get_abs_y(0.25))78 menu.items_per_col = 2
71 self.menu.set_item_spacing(self.get_abs_y(0.025))79 menu.visible_rows = 2
72 self.menu.set_visible_column_count(2)80 menu.visible_cols = 7
73 self.menu.set_row_count(7)81
74 self.menu.set_orientation(self.menu.VERTICAL)82 series = self.video_library.get_tv_series()
7583 series_list = [[serie.get_cover_art_url(), serie] for serie in series]
76 # Default texture84 menu.async_add_videos(series_list)
77 default_cover = Texture(self.theme.getImage("default_movie_art"))
78
79 all_series = self.video_library.get_tv_series()
80 for series in all_series:
81 if series.has_cover_art():
82 pix_buffer = gtk.gdk.pixbuf_new_from_file(
83 series.get_cover_art_url())
84 texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
85 else:
86 texture = clutter.CloneTexture(default_cover)
87
88 item = ImageMenuItem(0.1, 1.41, texture)
89 item.set_userdata(series)
90 self.menu.add_actor(item)
91
92 self.menu.set_position(self.get_abs_x(0.11), self.get_abs_y(0.2))
93 self.menu.set_active(False)
94 self.menu.show()
95
96 if self.config.show_effects():
97 self.menu.set_animate(True)
98
99 self.add(self.menu)
10085
101 # Create list indicator86 # Create list indicator
102 self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,87 self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,
103 ListIndicator.VERTICAL)88 ListIndicator.HORIZONTAL)
104 self.list_indicator.set_maximum(self.menu.get_number_of_items())89 self.list_indicator.set_maximum(len(series))
105 self.list_indicator.show()90 self.list_indicator.show()
106 self.add(self.list_indicator)91 self.add(self.list_indicator)
10792
@@ -115,64 +100,72 @@
115 self.series_info = Label(0.034, "subtitle", 0.2, 0.82, "")100 self.series_info = Label(0.034, "subtitle", 0.2, 0.82, "")
116 self.add(self.series_info)101 self.add(self.series_info)
117102
118 def _update_series_info(self):103 return menu
104
105 def _update_serie_info(self, event=None):
119 '''Update the series information labels.'''106 '''Update the series information labels.'''
120 if self.active:107 if self.active:
121 series = self.menu.get_current_menuitem().get_userdata()108 series = self.menu.selected_userdata
122 info = _("%(season)d Seasons\n%(episode)d Episodes") % {'season':109 info = _("%(season)d Seasons\n%(episode)d Episodes") % {'season':
123 series.get_number_of_seasons(), 'episode':110 series.get_number_of_seasons(), 'episode':
124 series.get_number_of_episodes()}111 series.get_number_of_episodes()}
125112
126 self.series_info.set_text(info)113 self.series_info.set_text(info)
127 self.series_title.set_text(series.get_title())114 self.series_title.set_text(series.get_title())
115 self.list_indicator.show()
116 self.list_indicator.set_current(self.menu.selected_index + 1)
128 else:117 else:
129 self.series_info.set_text("")118 self.series_info.set_text("")
130 self.series_title.set_text("")119 self.series_title.set_text("")
131120 self.list_indicator.hide()
132 def _update_menu(self, direction):
133 '''Update the menu based on the provided menu direction.'''
134 self.menu.move(direction)
135 self._update_series_info()
136 self.list_indicator.set_current(self.menu.get_current_position() + 1)
137 return False
138121
139 def _handle_up(self):122 def _handle_up(self):
140 '''Handle the up user event.'''123 '''Handle the up user event.'''
141 if self.menu.get_current_position() <= 6:124 if self.menu.on_top:
142 return True # Move control back to tab bar125 return True # Move control back to tab bar
143 else:126 else:
144 return self._update_menu(self.menu.UP)127 self.menu.up()
128 return False
145129
146 def _handle_down(self):130 def _handle_down(self):
147 '''Handle the down user event.'''131 '''Handle the down user event.'''
148 return self._update_menu(self.menu.DOWN)132 self.menu.down()
133 return False
149134
150 def _handle_left(self):135 def _handle_left(self):
151 '''Handle the left user event.'''136 '''Handle the left user event.'''
152 return self._update_menu(self.menu.LEFT)137 self.menu.left()
138 return False
153139
154 def _handle_right(self):140 def _handle_right(self):
155 '''Handle the right user event.'''141 '''Handle the right user event.'''
156 return self._update_menu(self.menu.RIGHT)142 self.menu.right()
143 return False
157144
158 def _handle_select(self):145 def _handle_select(self, event=None):
159 '''Handle the select user event.'''146 '''Handle the select user event.'''
160 series = self.menu.get_current_menuitem().get_userdata()147 series = self.menu.selected_userdata
161 kwargs = { 'tv_series' : series }148 kwargs = { 'tv_series' : series }
162 self.callback("tv_series", kwargs)149 self.callback("tv_series", kwargs)
163 return False150 return False
164151
165 def _on_activated(self, event=None):152 def _on_activated(self, event=None):
166 '''Tab activated.'''153 '''Tab activated.'''
167 self._update_series_info()154 if self.tab_group is not None:
168 self.menu.set_active(True)155 self.tab_group.active = False
169 self.menu.set_opacity(255)156 self.menu.active = True
157 self.active = True
158 self._update_serie_info()
170 return False159 return False
171160
172 def _on_deactivated(self, event=None):161 def _on_deactivated(self, event=None):
173 '''Tab deactivated.'''162 '''Tab deactivated.'''
174 self._update_series_info()163 self.active = False
175 self.menu.set_active(False)164 self.menu.active = False
176 self.menu.set_opacity(128)165 self._update_serie_info()
177 return False166 return False
178167
168 def _on_menu_filled(self, event=None):
169 '''Handles filled event.'''
170 self.throbber.hide()
171
179172
=== modified file 'entertainerlib/frontend/gui/tabs/tab.py'
--- entertainerlib/frontend/gui/tabs/tab.py 2009-05-12 17:18:10 +0000
+++ entertainerlib/frontend/gui/tabs/tab.py 2009-05-21 16:12:09 +0000
@@ -40,6 +40,7 @@
4040
41 # show/hide animation on the Tab41 # show/hide animation on the Tab
42 self.timeline = clutter.Timeline(30, 60)42 self.timeline = clutter.Timeline(30, 60)
43 self.timeline.connect('completed', self._on_timeline_completed)
43 self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)44 self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)
44 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)45 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)
45 self.behaviour.apply(self)46 self.behaviour.apply(self)
@@ -48,6 +49,7 @@
48 self._active = None49 self._active = None
49 self._visible = False50 self._visible = False
50 self.set_opacity(0)51 self.set_opacity(0)
52 self.hide()
5153
52 def _get_active(self):54 def _get_active(self):
53 '''active property getter.'''55 '''active property getter.'''
@@ -81,6 +83,7 @@
81 self._visible = boolean83 self._visible = boolean
8284
83 if boolean:85 if boolean:
86 self.show()
84 self.timeline.set_direction(clutter.TIMELINE_FORWARD)87 self.timeline.set_direction(clutter.TIMELINE_FORWARD)
85 self.timeline.rewind()88 self.timeline.rewind()
86 self.timeline.start()89 self.timeline.start()
@@ -90,6 +93,11 @@
9093
91 visible = property(_get_visible, _set_visible)94 visible = property(_get_visible, _set_visible)
9295
96 def _on_timeline_completed(self, timeline):
97 """Hides the Tab on the end of the fade out animation."""
98 if timeline.get_direction() == clutter.TIMELINE_BACKWARD:
99 self.hide()
100
93 def can_activate(self):101 def can_activate(self):
94 '''102 '''
95 Returns a boolean whether or not this tab can be activated. If tab103 Returns a boolean whether or not this tab can be activated. If tab
@@ -135,7 +143,7 @@
135 '''Dummy method for unimplemented handlers in subclass.'''143 '''Dummy method for unimplemented handlers in subclass.'''
136 return False144 return False
137145
138 def _handle_select(self):146 def _handle_select(self, event=None):
139 '''Dummy method for unimplemented handlers in subclass.'''147 '''Dummy method for unimplemented handlers in subclass.'''
140 return False148 return False
141149
142150
=== modified file 'entertainerlib/frontend/gui/tabs/tracks_tab.py'
--- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-07-14 11:06:21 +0000
@@ -1,14 +1,14 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Tracks tab for the artist screen'''2'''Tracks tab for the artist screen'''
33
4import clutter
5import pango4import pango
65
7from entertainerlib.frontend.gui.tabs.tab import Tab6from entertainerlib.frontend.gui.tabs.tab import Tab
8from entertainerlib.frontend.gui.widgets.label import Label7from entertainerlib.frontend.gui.widgets.label import Label
9from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator8from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
9from entertainerlib.frontend.gui.widgets.loading_animation import (
10 LoadingAnimation)
10from entertainerlib.frontend.gui.widgets.text_menu import TextMenu11from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
11from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem
1212
13class TracksTab(Tab):13class TracksTab(Tab):
14 '''Tab for the artist screen to show track listings'''14 '''Tab for the artist screen to show track listings'''
@@ -17,31 +17,23 @@
17 tab_title=_("Tracks")):17 tab_title=_("Tracks")):
18 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)18 Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
1919
20 self.menu = TextMenu(self.theme, self.config.show_effects())20 # Start the loading animation while the menu is loading
21 self.menu.set_item_size(self.get_abs_x(0.2928), self.get_abs_y(0.0781))21 self.throbber = LoadingAnimation(0.1, 0.1)
2222 self.throbber.show()
23 cursor = clutter.Rectangle()23 self.add(self.throbber)
24 cursor.set_color((0, 0, 0, 0))24
25 self.menu.set_cursor(cursor)25 self.menu = TextMenu(0.0586, 0.2083, 0.2928, 0.0781)
2626 self.menu.items_per_row = 3
27 for track in tracks:27 self.menu.visible_rows = 7
28 item = TextMenuItem(0.2928, 0.104, track.get_title())28 self.menu.visible_cols = 3
29 item.justified = True29 self.menu.active = False
30 item.set_userdata(track)30 self.menu.cursor = None
31 self.menu.add_actor(item)31 self.menu.items_anchor_west = False
32
33 self.menu.set_orientation(TextMenu.HORIZONTAL)
34 self.menu.set_row_count(7)
35 self.menu.set_visible_column_count(3)
36 self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.2083))
37 self.menu.set_active(False)
38 self.menu.show()
39
40 if self.config.show_effects():
41 self.menu.set_animate(True)
42
43 self.add(self.menu)32 self.add(self.menu)
4433
34 tracks_list = [[track.get_title(), None, track] for track in tracks]
35 self.menu.async_add_artists(tracks_list)
36
45 self.track_title = Label(0.045, "title", 0.22, 0.79, "")37 self.track_title = Label(0.045, "title", 0.22, 0.79, "")
46 self.track_title.set_ellipsize(pango.ELLIPSIZE_END)38 self.track_title.set_ellipsize(pango.ELLIPSIZE_END)
47 self.track_title.width = 0.36639 self.track_title.width = 0.366
@@ -56,78 +48,88 @@
56 self.add(self.track_length)48 self.add(self.track_length)
5749
58 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,50 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
59 ListIndicator.HORIZONTAL)51 ListIndicator.VERTICAL)
60 self.li.set_maximum(self.menu.get_number_of_items())52 self.li.set_maximum(len(tracks))
61 self.li.show()53 self.li.show()
62 self.add(self.li)54 self.add(self.li)
6355
64 self.connect('activated', self._on_activated)56 self.connect('activated', self._on_activated)
65 self.connect('deactivated', self._on_deactivated)57 self.connect('deactivated', self._on_deactivated)
58 self.menu.connect("moved", self._update_track_info)
59 self.menu.connect("selected", self._handle_select)
60 self.menu.connect("activated", self._on_activated)
61 self.menu.connect("filled", self._on_menu_filled)
6662
67 def can_activate(self):63 def can_activate(self):
68 '''Tracks tab will always be created from an existing artist with at64 '''Tracks tab will always be created from an existing artist with at
69 least one track.'''65 least one track.'''
70 return True66 return True
7167
72 def _update_track_info(self):68 def _update_track_info(self, event=None):
73 '''Update the track information labels'''69 '''Update the track information labels'''
74 if self.active:70 if self.active:
75 track = self.menu.get_current_menuitem().get_userdata()71 track = self.menu.selected_userdata
76 self.track_title.set_text(track.get_title())72 self.track_title.set_text(track.get_title())
77 self.track_length.set_text(str(track.get_length() / 60) + ":" + \73 self.track_length.set_text(str(track.get_length() / 60) + ":" + \
78 str(track.get_length() % 60).zfill(2))74 str(track.get_length() % 60).zfill(2))
79 self.track_number.set_text(_("Track %(track)s from %(album)s") % \75 self.track_number.set_text(_("Track %(track)s from %(album)s") % \
80 {'track': track.get_tracknumber(),76 {'track': track.get_tracknumber(),
81 'album': track.get_album().get_title()})77 'album': track.get_album().get_title()})
78 self.li.show()
79 self.li.set_current(self.menu.selected_index + 1)
82 else:80 else:
83 self.track_title.set_text("")81 self.track_title.set_text("")
84 self.track_length.set_text("")82 self.track_length.set_text("")
85 self.track_number.set_text("")83 self.track_number.set_text("")
8684 self.li.hide()
87 def _update_menu(self, direction):
88 '''Update the menu based on the provided menu direction.'''
89 self.menu.move(direction)
90 self._update_track_info()
91 self.li.set_current(self.menu.get_current_position() + 1)
92 return False
9385
94 def _handle_up(self):86 def _handle_up(self):
95 '''Handle the up user event.'''87 '''Handle the up user event.'''
96 if self.menu.get_cursor_position()[1] == 0:88 if self.menu.on_top:
97 return True # Move control back to tab bar89 return True # Move control back to tab bar
98 else:90 else:
99 return self._update_menu(self.menu.UP)91 self.menu.up()
92 return False
10093
101 def _handle_down(self):94 def _handle_down(self):
102 '''Handle the down user event.'''95 '''Handle the down user event.'''
103 return self._update_menu(self.menu.DOWN)96 self.menu.down()
97 return False
10498
105 def _handle_left(self):99 def _handle_left(self):
106 '''Handle the left user event.'''100 '''Handle the left user event.'''
107 return self._update_menu(self.menu.LEFT)101 self.menu.left()
102 return False
108103
109 def _handle_right(self):104 def _handle_right(self):
110 '''Handle the right user event.'''105 '''Handle the right user event.'''
111 return self._update_menu(self.menu.RIGHT)106 self.menu.right()
107 return False
112108
113 def _handle_select(self):109 def _handle_select(self, event=None):
114 '''Handle the select user event.'''110 '''Handle the select user event.'''
115 track = self.menu.get_current_menuitem().get_userdata()111 track = self.menu.selected_userdata
116 kwargs = { 'track' : track }112 kwargs = { 'track' : track }
117 self.callback("audio_play", kwargs)113 self.callback("audio_play", kwargs)
118 return False114 return False
119115
120 def _on_activated(self, event=None):116 def _on_activated(self, event=None):
121 '''Tab activated.'''117 '''Tab activated.'''
118 if self.tab_group is not None:
119 self.tab_group.active = False
120 self.menu.active = True
121 self.active = True
122 self._update_track_info()122 self._update_track_info()
123 self.menu.set_active(True)
124 self.menu.set_opacity(255)
125 return False123 return False
126124
127 def _on_deactivated(self, event=None):125 def _on_deactivated(self, event=None):
128 '''Tab deactivated.'''126 '''Tab deactivated.'''
127 self.active = False
128 self.menu.active = False
129 self._update_track_info()129 self._update_track_info()
130 self.menu.set_active(False)
131 self.menu.set_opacity(128)
132 return False130 return False
133131
132 def _on_menu_filled(self, event=None):
133 '''Handles filled event.'''
134 self.throbber.hide()
135
134136
=== modified file 'entertainerlib/frontend/gui/tabs/video_clips_tab.py'
--- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-05-14 03:22:31 +0000
+++ entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-07-14 11:06:21 +0000
@@ -3,15 +3,14 @@
33
4import os4import os
55
6import gtk
7import pango6import pango
87
9from entertainerlib.frontend.gui.tabs.tab import Tab8from entertainerlib.frontend.gui.tabs.tab import Tab
10from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu9from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
11from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem
12from entertainerlib.frontend.gui.widgets.label import Label10from entertainerlib.frontend.gui.widgets.label import Label
13from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator11from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
14from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture12from entertainerlib.frontend.gui.widgets.loading_animation import (
13 LoadingAnimation)
1514
16class VideoClipsTab(Tab):15class VideoClipsTab(Tab):
17 """16 """
@@ -35,7 +34,17 @@
35 if self.video_library.get_number_of_video_clips() == 0:34 if self.video_library.get_number_of_video_clips() == 0:
36 self._create_empty_library_notice()35 self._create_empty_library_notice()
37 else:36 else:
38 self._create_menu()37 # Start the loading animation while the menu is loading
38 self.throbber = LoadingAnimation(0.7, 0.1)
39 self.throbber.show()
40 self.add(self.throbber)
41
42 self.menu = self._create_menu()
43 self.add(self.menu)
44 self.menu.connect("moved", self._update_clip_info)
45 self.menu.connect("selected", self._handle_select)
46 self.menu.connect("activated", self._on_activated)
47 self.menu.connect("filled", self._on_menu_filled)
3948
40 self.connect('activated', self._on_activated)49 self.connect('activated', self._on_activated)
41 self.connect('deactivated', self._on_deactivated)50 self.connect('deactivated', self._on_deactivated)
@@ -65,33 +74,19 @@
65 Create a view that is displayed when there are indexed clips in74 Create a view that is displayed when there are indexed clips in
66 the video library.75 the video library.
67 """76 """
68 self.menu = ImageMenu(self.theme)77 menu = ImageMenu(0.04, 0.16, 0.23, self.y_for_x(0.23) * 0.7)
69 self.menu.set_item_size(self.get_abs_x(0.233), self.get_abs_y(0.25))78 menu.items_per_col = 2
70 self.menu.set_item_spacing(self.get_abs_y(0.025))79 menu.visible_rows = 2
71 self.menu.set_visible_column_count(2)80 menu.visible_cols = 4
72 self.menu.set_row_count(3)
73 self.menu.set_orientation(self.menu.VERTICAL)
7481
75 clips = self.video_library.get_video_clips()82 clips = self.video_library.get_video_clips()
76 for clip in clips:83 clips_list = [[clip.get_thumbnail_url(), clip] for clip in clips]
77 pix_buffer = gtk.gdk.pixbuf_new_from_file(clip.get_thumbnail_url())84 menu.async_add_clips(clips_list)
78 texture = RoundedTexture(0.0, 0.0, 0.233, 0.25, pix_buffer)
79 item = ImageMenuItem(0.233, 0.5625, texture)
80 item.set_userdata(clip)
81 self.menu.add_actor(item)
82
83 self.menu.set_position(self.get_abs_x(0.15), self.get_abs_y(0.2))
84 self.menu.set_active(False)
85
86 if self.config.show_effects():
87 self.menu.set_animate(True)
88
89 self.add(self.menu)
9085
91 # Create list indicator86 # Create list indicator
92 self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045,87 self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045,
93 ListIndicator.VERTICAL)88 ListIndicator.HORIZONTAL)
94 self.list_indicator.set_maximum(self.menu.get_number_of_items())89 self.list_indicator.set_maximum(len(clips))
95 self.list_indicator.show()90 self.list_indicator.show()
96 self.add(self.list_indicator)91 self.add(self.list_indicator)
9792
@@ -107,46 +102,48 @@
107 self.clip_info.width = 0.5102 self.clip_info.width = 0.5
108 self.add(self.clip_info)103 self.add(self.clip_info)
109104
110 def _update_clip_info(self):105 return menu
106
107 def _update_clip_info(self, event=None):
111 '''Update the VideoClip information labels.'''108 '''Update the VideoClip information labels.'''
112 if self.active:109 if self.active:
113 clip = self.menu.get_current_menuitem().get_userdata()110 clip = self.menu.selected_userdata
114 (folder, filename) = os.path.split(clip.get_filename())111 (folder, filename) = os.path.split(clip.get_filename())
115 self.clip_title.set_text(filename)112 self.clip_title.set_text(filename)
116 self.clip_info.set_text(folder)113 self.clip_info.set_text(folder)
114 self.list_indicator.show()
115 self.list_indicator.set_current(self.menu.selected_index + 1)
117 else:116 else:
118 self.clip_title.set_text("")117 self.clip_title.set_text("")
119 self.clip_info.set_text("")118 self.clip_info.set_text("")
120119 self.list_indicator.hide()
121 def _update_menu(self, direction):
122 '''Update the menu based on the provided menu direction.'''
123 self.menu.move(direction)
124 self._update_clip_info()
125 self.list_indicator.set_current(self.menu.get_current_position() + 1)
126 return False
127120
128 def _handle_up(self):121 def _handle_up(self):
129 '''Handle the up user event.'''122 '''Handle the up user event.'''
130 if self.menu.get_current_position() <= 2:123 if self.menu.on_top:
131 return True # Move control back to tab bar124 return True # Move control back to tab bar
132 else:125 else:
133 return self._update_menu(self.menu.UP)126 self.menu.up()
127 return False
134128
135 def _handle_down(self):129 def _handle_down(self):
136 '''Handle the down user event.'''130 '''Handle the down user event.'''
137 return self._update_menu(self.menu.DOWN)131 self.menu.down()
132 return False
138133
139 def _handle_left(self):134 def _handle_left(self):
140 '''Handle the left user event.'''135 '''Handle the left user event.'''
141 return self._update_menu(self.menu.LEFT)136 self.menu.left()
137 return False
142138
143 def _handle_right(self):139 def _handle_right(self):
144 '''Handle the right user event.'''140 '''Handle the right user event.'''
145 return self._update_menu(self.menu.RIGHT)141 self.menu.right()
142 return False
146143
147 def _handle_select(self):144 def _handle_select(self, event=None):
148 '''Handle the select user event.'''145 '''Handle the select user event.'''
149 clip = self.menu.get_current_menuitem().get_userdata()146 clip = self.menu.selected_userdata
150 self.media_player.set_media(clip)147 self.media_player.set_media(clip)
151 self.media_player.play()148 self.media_player.play()
152 self.callback("video_osd")149 self.callback("video_osd")
@@ -154,15 +151,21 @@
154151
155 def _on_activated(self, event=None):152 def _on_activated(self, event=None):
156 '''Tab activated.'''153 '''Tab activated.'''
154 if self.tab_group is not None:
155 self.tab_group.active = False
156 self.menu.active = True
157 self.active = True
157 self._update_clip_info()158 self._update_clip_info()
158 self.menu.set_active(True)
159 self.menu.set_opacity(255)
160 return False159 return False
161160
162 def _on_deactivated(self, event=None):161 def _on_deactivated(self, event=None):
163 '''Tab deactivated.'''162 '''Tab deactivated.'''
163 self.active = False
164 self.menu.active = False
164 self._update_clip_info()165 self._update_clip_info()
165 self.menu.set_active(False)
166 self.menu.set_opacity(128)
167 return False166 return False
168167
168 def _on_menu_filled(self, event=None):
169 '''Handles filled event.'''
170 self.throbber.hide()
171
169172
=== modified file 'entertainerlib/frontend/gui/widgets/base.py'
--- entertainerlib/frontend/gui/widgets/base.py 2009-04-28 22:30:06 +0000
+++ entertainerlib/frontend/gui/widgets/base.py 2009-05-24 14:28:19 +0000
@@ -21,3 +21,12 @@
21 result = int(self.config.get_stage_height() * percentage)21 result = int(self.config.get_stage_height() * percentage)
22 return result22 return result
2323
24 def y_for_x(self, percentage):
25 """
26 Returns the percentage of stage height giving the same number of pixels
27 than `percentage` applied to the stage width.
28 """
29 stage_ratio = float(self.config.get_stage_width())
30 stage_ratio /= self.config.get_stage_height()
31 return stage_ratio * percentage
32
2433
=== modified file 'entertainerlib/frontend/gui/widgets/grid_menu.py'
--- entertainerlib/frontend/gui/widgets/grid_menu.py 2009-05-10 06:24:43 +0000
+++ entertainerlib/frontend/gui/widgets/grid_menu.py 2009-05-28 19:12:05 +0000
@@ -1,672 +1,561 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''GridMenu contains a grid of textures and allows user to select one'''2'''GridMenu contains a grid of MenuItem based widgets.'''
33
4import clutter4import clutter
55import gobject
6class GridMenu(clutter.Group):6import math
7
8from entertainerlib.frontend.gui.widgets.base import Base
9from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer
10
11class GridMenu(Base, clutter.Group):
7 """12 """
8 GridMenu widget13 GridMenu widget
9
10 This is a flexible menu widget that supports horizontal, vertical,
11 scrollable, multirow menus. Real menus should inherit this class
12 and override few methods.
13 """14 """
1415 __gsignals__ = {
15 # Orientation constants16 'activated' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
16 HORIZONTAL = 017 'moved' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
17 VERTICAL = 118 'selected' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
1819 }
19 # Direction constants20
20 UP = 221 MODE_NONE = 0
21 DOWN = 322 MODE_SELECT = 1
22 LEFT = 423 MODE_SEEK = 2
23 RIGHT = 524
2425 def __init__(self, x=0, y=0, item_width=200, item_height=100):
25 # Cursor placement (Is cursor drawn above or below of all menuitems)26 Base.__init__(self)
26 BELOW = 6
27 ABOVE = 7
28
29 def __init__(self, orientation = 1): # Default orientation is VERTICAL
30 """
31 Initialize GridMenu widget
32 @param orientation: Orientation of the menu. Use class constatants.
33 """
34 clutter.Group.__init__(self)27 clutter.Group.__init__(self)
3528
36 self.itemgroup = clutter.Group() # Group for menuitems29 self.motion_duration = 100
37 self.items = [] # List of Menuitems30 self.cursor_below = True
38 self.orientation = orientation # Menu orientation31 self._active = None
39 self.row_count = 1 # Items per row32 self._is_vertical = True
40 self.visible_cols = 8 # Number of visible columns33 self.items_anchor_west = False
41 self.cell_height = 100 # Menuitem height
42 self.cell_width = 100 # Menuitem width
43 self.cell_spacing = 0 # Space between items
44 self.logical_position = 0 # Logical position (in item list)
45 self.cursor_position = (0, 0) # Cursor position (on screen)
46 self.content_position = (0, 0) # Content position
47 self.animate = False # Should we animate this widget
48 self.cursor = None # Cursor (clutter.Actor)
49 self.cursor_placement = 7 # Cursor placement (Default: Above)
50 self.active = True # Is this menu active
51 self.content_timeline = None # Content scroll timeline
52 self.cursor_timeline = None # Cursor move timeline
53
54 self.add(self.itemgroup)
55
56 # Default red transparent cursor
57 c = clutter.Rectangle()
58 c.set_color((255, 0, 0, 128))
59 self.set_cursor(c)
60
61 def set_cursor(self, cursor):
62 """
63 Set cursor actor. This is usually a Texture or clutter.Rectangle.
64 @param cursor: clutter.Actor - Cursor graphics
65 """
66 # Remove old if exists
67 if self.cursor is not None:
68 self.remove(self.cursor)
69
70 # Set new cursor
71 self.cursor = cursor
72
73 # If widget is currently active display cursor
74 if self.active:
75 self.cursor.show()
76
77 self.add(self.cursor)
78 self.cursor.set_size(self.cell_width, self.cell_height)
79 self._scale_cursor()
80
81 # Set cursor placement
82 if self.cursor_placement == self.BELOW:
83 self.cursor.lower_bottom()
84 elif self.cursor_placement == self.ABOVE:
85 self.cursor.raise_top()
86
87 def set_cursor_placement(self, placement):
88 """
89 Set cursor placement. Is cursor drawn below or above of all menuitems.
90 @param placement: GridMenu.BELOW or GridMenu.ABOVE
91 """
92 self.cursor_placement = placement
93 if self.cursor_placement == self.BELOW:
94 self.cursor.lower_bottom()
95 elif self.cursor_placement == self.ABOVE:
96 self.cursor.raise_top()
97
98 def set_active(self, boolean):
99 """
100 Set menu widget to active / disabled. Please see API references of
101 the self._animate_items_on_state_change() method. It handels all actor
102 changes when menu widget state changes.
103 """
104 self.active = boolean
105 self._animate_items_on_state_change()
106
107 def is_active(self):
108 """
109 Is this menu active?
110 @return: True if menu is active, otherwise False
111 """
112 return self.active
113
114 def set_animate(self, boolean):
115 """
116 Enable/disable widget animation.
117 @param boolean: True if widget should be animated, otherwise False
118 """
119 self.animate = boolean
120
121 def add_actor(self, item):
122 """
123 Add menuitem to this menu.
124 @param item: clutter.Actor object
125 """
126 #item.set_size(self.cell_width, self.cell_height)
127 self._scale_menuitem(item) # Scale item
128 self.items.append(item) # Add to logical list
129 self.itemgroup.add(item) # Add to menuitem group (visible list)
130 self._update_menuitem_position(item, len(self.items) - 1)
131
132 # Update cursor
133 if self.cursor_placement == self.BELOW:
134 self.cursor.lower_bottom()
135 elif self.cursor_placement == self.ABOVE:
136 self.cursor.raise_top()
137
138 # If this is a first item in the menu.
139 if len(self.items) == 1:
140 self._update_selected_item(0)
141 self.set_visible_column_count(self.visible_cols)
142
143 def remove_actor(self, item):
144 """
145 Remove menuitem from this menu.
146 @param item: clutter.Actor object
147 """
148 try:
149 self.items.remove(item) # Remove from logical list
150 self.itemgroup.remove(item) # Remove from visible list group
151 for i in range(len(self.items)):
152 self._update_menuitem_position(self.items[i], i)
153 except:
154 raise Exception("Couldn't remove menuitem. Item doesn't exist.")
155
156 def remove_all(self):
157 """
158 Remove all menuitems from this menu.
159 """
160 self.items = []34 self.items = []
161 self.itemgroup.remove_all()35
16236 self._item_width = item_width
163 def select(self, logical_position):37 self._item_height = item_height
164 '''Selects the item at the passed logical_position.'''38 self._item_width_abs = self.get_abs_x(item_width)
165 # pylint: disable-msg=W061239 self._item_height_abs = self.get_abs_y(item_height)
166 target = ( int(logical_position / self.row_count),40 self._dx = int(self._item_width_abs / 2)
167 int(logical_position % self.row_count))41 self._dy = int(self._item_height_abs / 2)
16842
169 move_y = self.cursor_position[0] - target[0]43 self._selected_index = 0
170 move_x = self.cursor_position[1] - target[1]44 self.items_per_row = 10
17145 self.items_per_col = 10
172 #Move Vertically46 self._visible_rows = 3
173 if move_y > 0:47 self._visible_cols = 5
174 for i in xrange(move_y):48
175 self._move_cursor(self.UP)49 self._moving_group_x = 0
176 else:50 self._moving_group_y = 0
177 for i in xrange(-move_y):51 self._moving_group = clutter.Group()
178 self._move_cursor(self.DOWN)52 self.add(self._moving_group)
17953
180 #Move Horizontally54 self._moving_group_timeline = clutter.Timeline(fps=200, duration=200)
181 if move_x > 0:55 moving_group_alpha = clutter.Alpha(self._moving_group_timeline,
182 for i in xrange(move_x):56 clutter.smoothstep_inc_func)
183 self._move_cursor(self.RIGHT)57 self._moving_group_behaviour = clutter.BehaviourPath(moving_group_alpha)
184 else:58 self._moving_group_behaviour.apply(self._moving_group)
185 for i in xrange(-move_x):59
186 self._move_cursor(self.LEFT)60 self._cursor_x = 0
18761 self._cursor_y = 0
188 def get_n_children(self):62 self._cursor = None
189 """63 self._cursor_timeline = clutter.Timeline(fps=200, duration=200)
190 Override clutter.Group method. Returns number of menuitems.64 cursor_alpha = clutter.Alpha(self._cursor_timeline,
191 """65 clutter.sine_inc_func)
192 return len(self.items)66 self._cursor_behaviour = clutter.BehaviourPath(cursor_alpha)
19367
194 def get_nth_child(self, index):68 self._event_rect = clutter.Rectangle()
195 """69 self._event_rect.set_opacity(0)
196 Override clutter.Group method. Returns one menuitem.70 self._event_rect.set_color(clutter.color_parse('Orange'))
197 """71 self.add(self._event_rect)
198 return self.items[index]72
19973 self._motion_buffer = MotionBuffer()
200 def get_number_of_items(self):74 self._event_mode = self.MODE_NONE
201 """75 self._motion_handler = 0
202 Get number of menuitems.76 self._seek_step_x = 0
203 @return: Integer77 self._seek_step_y = 0
204 """78 gobject.timeout_add(200, self._internal_timer_callback)
205 return len(self.items)79
20680 #XXX: Samuel Buffet
207 def get_current_position(self):81 # This rectangle is used to grab events as it turns out that their
208 """82 # might be a bug in clutter 0.8 or python-clutter 0.8.
209 Get logical position of the menu cursor.83 # It may be avoided with next release of clutter.
210 """84 self._event_rect.set_reactive(True)
211 return self.logical_position85 self._event_rect.connect('button-press-event',
21286 self._on_button_press_event)
213 def get_current_menuitem(self):87 self._event_rect.connect('button-release-event',
214 """88 self._on_button_release_event)
215 Get current menuitem. (Menuitem under menu cursor)89 self._event_rect.connect('scroll-event', self._on_scroll_event)
216 @return: clutter.Actor90
217 """91 self.set_position(self.get_abs_x(x), self.get_abs_y(y))
218 return self.items[self.logical_position]92
21993 @property
220 def get_cursor_position(self):94 def count(self):
221 """95 """Return the number of items."""
222 Get cursor position. This is a (x,y) coordinate of the cursor. Cursor96 return len(self.items)
223 position is always in displayble area.97
224 @return: (x,y) tuple98 @property
225 """99 def on_top(self):
226 return self.cursor_position100 """Return True if the selected item is currently on the top."""
227101 selected_row = self._index_to_xy(self._selected_index)[1]
228 def set_orientation(self, orientation):102 if selected_row == 0:
229 """103 return True
230 Set widget orientation. This defines which way widget is scrolled when104 else:
231 all items don't fit into the widget area at the same time.105 return False
232 @param orientation: GridMenu.HORIZONTAL or GridMenu.VERTICAL106
233 """107 @property
234 if orientation < 0 or orientation > 1:108 def on_bottom(self):
235 raise Exception("Invalid orientation")109 """Return True if the selected item is currently on the bottom."""
236 self.orientation = orientation110 selected_row = self._index_to_xy(self._selected_index)[1]
237 for i in range(len(self.items)):111 end_row = self._index_to_xy(self.count - 1)[1]
238 self._update_menuitem_position(self.items[i], i)112 if self._is_vertical:
239113 if selected_row == end_row:
240 def get_orientation(self):114 return True
241 """115 else:
242 Get menu orientation.116 return False
243 @return: GridMenu.VERTICAL or GridMenu.HORIZONTAL (Integer constant)117 else:
244 """118 if selected_row == self.items_per_col - 1:
245 return self.orientation119 return True
246120 else:
247 def set_row_count(self, count):121 return False
248 """122
249 Set how many items there should be on one row. Notice that row's123 @property
250 direction depends on widget's orientation. For example, 'normal menu'124 def on_left(self):
251 would be VERTICAL with a row count 1.125 """Return True if the selected item is currently on the left."""
252 @param count: Number of items per row126 selected_col = self._index_to_xy(self._selected_index)[0]
253 """127 if selected_col == 0:
254 if count < 1:128 return True
255 raise Exception("Row count must be a positive value.")129 else:
256 self.row_count = count130 return False
257 for i in range(len(self.items)):131
258 self._update_menuitem_position(self.items[i], i)132 @property
259133 def on_right(self):
260 def get_row_count(self):134 """Return True if the selected item is currently on the right."""
261 """135 selected_col = self._index_to_xy(self._selected_index)[0]
262 Get row count.136 end_col = self._index_to_xy(self.count - 1)[0]
263 @return: Integer137 if not self._is_vertical:
264 """138 if selected_col == end_col:
265 return self.row_count139 return True
266140 else:
267 def set_visible_column_count(self, count):141 return False
268 """142 else:
269 Set how many columns are allowed to be visible at the same time. Column143 if selected_col == self.items_per_row - 1:
270 direction depends on widget orientation. For example, if menu is144 return True
271 VERTICAL, row_count is 1 and visible columns is 8, then there are at145 else:
272 most 8 visible items which are scrolled vertically.146 return False
273 """147
274 if count < 1:148 @property
275 raise Exception("Only positive values allowed!")149 def selected_item(self):
276 self.visible_cols = count150 """Return the selected MenuItem."""
277151 if self.count == 0:
278 if self.orientation == self.VERTICAL:152 return None
279 w = (self.cell_width + self.cell_spacing) * self.row_count153 else:
280 h = count * (self.cell_height + self.cell_spacing)154 return self.items[self._selected_index]
281 else:155
282 w = count * (self.cell_width + self.cell_spacing)156 @property
283 h = (self.cell_height + self.cell_spacing) * self.row_count157 def selected_userdata(self):
284158 """Return userdata of the MenuItem."""
285 self.remove_clip()159 item = self.selected_item
286 #print "clip area: (width: " + str(w) + " and height: " + str(h)160 if item is None:
287 self.set_clip(0, 0, w, h)161 return None
288162 else:
289 def get_visible_column_count(self):163 return item.userdata
290 """164
291 Get the number of visible columns.165 def _get_active(self):
292 @return: Integer166 """Active property getter."""
293 """167 return self._active
294 return self.visible_cols168
295169 def _set_active(self, boolean):
296 def set_item_spacing(self, space):170 """Active property setter."""
297 """171 if self._active == boolean:
298 Set how many pixels should be left between menu items.172 return
299 @param space: Item spacing in pixels (0 or higher)173
300 """174 self._active = boolean
301 if space < 0:175 if boolean:
302 raise Exception("Spacing must be a positive integer.")176 if self._cursor is not None:
303 self.cell_spacing = space177 self._cursor.show()
304 for i in range(len(self.items)):178 if self.selected_item is not None:
305 self._update_menuitem_position(self.items[i], i)179 self.selected_item.animate_in()
306180 self.emit('activated')
307 def set_item_height(self, height):181 self.set_opacity(255)
308 """182 else:
309 Set the height of the one cell in the grid.183 if self._cursor is not None:
310 @param height: Height in pixels (positive value)184 self._cursor.hide()
311 """185 if self.selected_item is not None:
312186 self.selected_item.animate_out()
313 if height < 1:187 self.set_opacity(128)
314 raise Exception("Height must be a positive value")188
315189 active = property(_get_active, _set_active)
316 self.cell_height = height190
317191 def _get_horizontal(self):
318 # Update cursor192 """horizontal property getter."""
319 self._scale_cursor()193 return not self._is_vertical
320194
321 # Update menuitems195 def _set_horizontal(self, boolean):
322 for item in self.items:196 """horizontal property setter."""
323 self._scale_menuitem(item)197 self._is_vertical = not boolean
324 for i in range(len(self.items)):198
325 self._update_menuitem_position(self.items[i], i)199 horizontal = property(_get_horizontal, _set_horizontal)
326200
327 def set_item_width(self, width):201 def _get_vertical(self):
328 """202 """vertical property getter."""
329 Set the width of the one cell in the grid.203 return self._is_vertical
330 @param height: Width in pixels (positive value)204
331 """205 def _set_vertical(self, boolean):
332 if width < 1:206 """vertical property setter."""
333 raise Exception("Width must be a positive value")207 self._is_vertical = boolean
334208
335 self.cell_width = width209 vertical = property(_get_vertical, _set_vertical)
336210
337 # Update cursor211 def _get_selected_index(self):
338 self._scale_cursor()212 """selected_index property getter."""
339213 return self._selected_index
340 # Update menuitems214
341 for item in self.items:215 def _set_selected_index(self, index, duration=None):
342 self._scale_menuitem(item)216 """selected_index property setter."""
343 for i in range(len(self.items)):217 # Xc, Yc : coordinates of the cursor relatively to the moving group.
344 self._update_menuitem_position(self.items[i], i)218 # xc, yc : coordinates of the cursor relatively to the menu.
345219 # xm, ym : coordinates of the moving_group relatively to the menu.
346 def set_item_size(self, width, height):220 # Xc = xc - xm
347 """221 # Yc = yc - ym
348 Set the width of the one cell in the grid.222
349 @param height: Width in pixels (positive value)223 if self._selected_index == index or \
350 """224 index < 0 or \
351 if width < 1 or height < 1 :225 index > self.count - 1 or \
352 raise Exception("Width and height must be positive values!")226 self._moving_group_timeline.is_playing() or \
353227 self._cursor_timeline.is_playing():
354 self.cell_width = width228 return
355 self.cell_height = height229
356230 #start select/unselect animations on both items
357 # Update clipping231 self.items[self._selected_index].animate_out()
358 #self.set_visible_column_count(self.visible_cols)232 self.items[index].animate_in()
359233
360 # Update cursor234 (Xc, Yc) = self._index_to_xy(index)
361 self._scale_cursor()235
362236 xm = self._moving_group_x
363 # Update menuitems237 ym = self._moving_group_y
364 for item in self.items:238
365 self._scale_menuitem(item)239 xc = Xc + xm
366 for i in range(len(self.items)):240 yc = Yc + ym
367 self._update_menuitem_position(self.items[i], i)241
368242 if xc == self.visible_cols - 1 and \
369 def move(self, direction):243 xm > self.visible_cols -self.items_per_row:
370 """244 xc -= 1
371 Move to given direction. Cursor is moved or grid content is scrolled if245 xm -= 1
372 needed.246
373 @param direction: GridMenu class variables (UP, DOWN, LEFT, RIGHT)247 if xc == 0 and xm < 0:
374 """248 xc += 1
375 if not self._is_move_legal(direction):249 xm += 1
376 return250
377251 if yc == self.visible_rows - 1 and \
378 # Current position of the content and cursor252 ym > self.visible_rows -self.items_per_col:
379 cur_x, cur_y = self.cursor_position253 yc -= 1
380 con_x, con_y = self.content_position254 ym -= 1
381255
382 # Define limit value for cursor and content move256 if yc == 0 and ym < 0:
383 max_row = len(self.items) / self.row_count257 yc += 1
384 if (len(self.items) % self.row_count) != 0:258 ym += 1
385 max_row = max_row + 1259
386260 if duration is None:
387 content_scroll_max = max_row - self.visible_cols261 duration = self.motion_duration
388262
389 if self.visible_cols > max_row:263 self._move_cursor(xc, yc, duration)
390 # There are less rows than allowed to be visible at the same time264 self._move_moving_group(xm, ym, duration)
391 cursor_scroll_max = max_row - 1265 self._selected_index = index
392 else:266
393 cursor_scroll_max = self.visible_cols - 1267 self.emit('moved')
394268
395 # Menu has a vertical orientation269 selected_index = property(_get_selected_index, _set_selected_index)
396 if self.orientation == self.VERTICAL:270
397 if direction == self.UP:271 def _get_visible_rows(self):
398 if cur_y == 0:272 """visible_rows property getter."""
399 if con_y > 0:273 return self._visible_rows
400 self._move_content(1, self.DOWN)274
401 else:275 def _set_visible_rows(self, visible_rows):
402 self._move_cursor(self.UP)276 """visible_rows property setter."""
403 elif direction == self.DOWN:277 self._visible_rows = visible_rows
404 if cur_y < cursor_scroll_max:278 self._clip()
405 self._move_cursor(self.DOWN)279
406 else:280 visible_rows = property(_get_visible_rows, _set_visible_rows)
407 if con_y < content_scroll_max:281
408 self._move_content(1, self.UP)282 def _get_visible_cols(self):
409 elif direction == self.LEFT:283 """visible_cols property getter."""
410 if cur_x > 0:284 return self._visible_cols
411 self._move_cursor(self.LEFT)285
412 elif direction == self.RIGHT:286 def _set_visible_cols(self, visible_cols):
413 if cur_x < self.row_count - 1:287 """visible_cols property setter."""
414 self._move_cursor(self.RIGHT)288 self._visible_cols = visible_cols
415289 self._clip()
416 # Menu has a horizontal orientation290
417 else:291 visible_cols = property(_get_visible_cols, _set_visible_cols)
418 if direction == self.UP:292
419 if cur_y > 0:293 def _get_cursor(self):
420 self._move_cursor(self.UP)294 """cursor property getter."""
421 elif direction == self.DOWN:295 return self._cursor
422 if cur_y < self.row_count - 1:296
423 self._move_cursor(self.DOWN)297 def _set_cursor(self, cursor):
424 elif direction == self.LEFT:298 """cursor property setter."""
425 if cur_x == 0:299 if self._cursor is not None:
426 if con_x > 0:300 self.remove(self._cursor)
427 self._move_content(1, self.RIGHT)301
428 else:302 self._cursor = cursor
429 self._move_cursor(self.LEFT)303
430 elif direction == self.RIGHT:304 if self._cursor is not None:
431 if cur_x < cursor_scroll_max:305 self.add(self._cursor)
432 self._move_cursor(self.RIGHT)306 if self._active:
433 else:307 self._cursor.show()
434 if con_x < content_scroll_max:308 else:
435 self._move_content(1, self.LEFT)309 self._cursor.hide()
436310
437 old_position = self.logical_position311 if self.cursor_below:
438312 self._cursor.lower_bottom()
439 # Calculate logical position313 else:
440 if self.orientation == self.VERTICAL:314 self._cursor.raise_top()
441 tmp = (self.cursor_position[1] +315
442 self.content_position[1]) * self.row_count316 self._cursor.set_size(int(self._item_width_abs),
443 self.logical_position = tmp + self.cursor_position[0]317 int(self._item_height_abs))
444 else:318 self._cursor.set_anchor_point(self._dx, self._dy)
445 tmp = (self.cursor_position[0] +319 self._cursor.set_position(self._dx, self._dy)
446 self.content_position[0]) * self.row_count320
447 self.logical_position = tmp + self.cursor_position[1]321 self._cursor_behaviour.apply(self._cursor)
448322
449 self._update_selected_item(self.logical_position, old_position)323 cursor = property(_get_cursor, _set_cursor)
450324
451 def _is_move_legal(self, direction):325 def _clip(self):
452 """326 """Updates the clipping region."""
453 Check if move is legal. This prevents cursor from leaving menu items.327 self.set_clip(0, 0, self._visible_cols * self._item_width_abs,
454 This could happen when grid's last row is not "full".328 self._visible_rows * self._item_height_abs)
455 @param direction: GridMenu class variables (UP, DOWN, LEFT, RIGHT)329
456 @return: True if move can be executed, otherwise False330 self._event_rect.set_size(self._visible_cols * self._item_width_abs,
457 """331 self._visible_rows * self._item_height_abs)
458 # If last row is "full" we can't go wrong332
459 if len(self.items) % self.row_count == 0:333 def raw_add_item(self, item):
460 return True334 """Add an item."""
461335 self._moving_group.add(item)
462 # -1 because rows start from 0336 self.items.append(item)
463 last_full_row = len(self.items) / self.row_count -1337
464 items_on_last_row = len(self.items) % self.row_count338 (x, y) = self._index_to_xy(self.count - 1)
465339
466 # Checking for vertical menu340 if self.items_anchor_west:
467 if self.orientation == self.VERTICAL:341 item.set_position(x * self._item_width_abs,
468 current_row = self.cursor_position[1] + self.content_position[1]342 y * self._item_height_abs + self._dy)
469 if current_row == last_full_row and direction == self.DOWN:343 else:
470 if self.cursor_position[0] + 1 > items_on_last_row:344 item.move_anchor_point(self._dx, self._dy)
471 return False345 item.set_position(x * self._item_width_abs + self._dx,
472 if current_row == last_full_row + 1 and direction == self.RIGHT:346 y * self._item_height_abs + self._dy)
473 if self.logical_position == len(self.items) - 1:347
474 return False348 if self._is_vertical:
475 # Checking for horizontal menu349 self.items_per_col = y + 1
476 else:350 else:
477 current_row = self.cursor_position[0] + self.content_position[0]351 self.items_per_row = x + 1
478 if current_row == last_full_row and direction == self.RIGHT:352
479 if self.cursor_position[1] + 1 > items_on_last_row:353 if self.cursor_below:
480 return False354 item.raise_top()
481 if current_row == last_full_row + 1 and direction == self.DOWN:355 else:
482 if self.logical_position == len(self.items) - 1:356 item.lower_bottom()
483 return False357
484 return True358 def _index_to_xy(self, index):
485359 """Return the coordinates of an element associated to its index."""
486 def _move_cursor(self, direction):360 if self._is_vertical:
487 """361 r = index / float(self.items_per_row)
488 Move cursor on grid.362 y = int(math.modf(r)[1])
489 """363 x = int(index - y * self.items_per_row)
490 # Change logical position364 else:
491 x, y = self.cursor_position365 r = index / float(self.items_per_col)
492 if direction == self.UP:366 x = int(math.modf(r)[1])
493 self.cursor_position = (x, y-1)367 y = int(index - x * self.items_per_col)
494 elif direction == self.DOWN:368
495 self.cursor_position = (x, y+1)369 return (x, y)
496 elif direction == self.LEFT:370
497 self.cursor_position = (x-1, y)371 def _move_moving_group(self, x, y, duration):
498 elif direction == self.RIGHT:372 """Moves the moving_group to x, y coordinates."""
499 self.cursor_position = (x+1, y)373 if (x, y) == (self._moving_group_x, self._moving_group_y):
500374 return
501 # Change cursor texture position375
502 x_offset = self.cell_width + self.cell_spacing376 self._moving_group_behaviour.clear()
503 y_offset = self.cell_height + self.cell_spacing377
504378 start_knot = (self._moving_group_x * self._item_width_abs,
505 # BELOW IS ANIMATION CODE FOR MENU CURSOR. SHOULD CURSOR BE ANIMATED?379 self._moving_group_y * self._item_height_abs)
506# if self.animate:380 stop_knot = (x * self._item_width_abs,
507# # Finish previous animation before new381 y * self._item_height_abs)
508# if self.cursor_timeline is not None and (382
509# self.cursor_timeline.is_playing():383 self._moving_group_behaviour.append_knots(start_knot, stop_knot)
510# self.cursor_timeline.pause()384
511# self.cursor_timeline.advance(4)385 self._moving_group_x, self._moving_group_y = x, y
512#386 self._moving_group_timeline.set_duration(duration)
513# self.cursor_timeline = clutter.Timeline(4, 26)387 self._moving_group_timeline.start()
514# alpha = clutter.Alpha(self.cursor_timeline, clutter.ramp_inc_func)388
515#389 def _move_cursor(self, x, y, duration):
516# x = self.cursor.get_x()390 """
517# y = self.cursor.get_y()391 Moves the cursor to x, y coordinates.
518# if direction == self.UP:392 The motion is applied to the center of the cursor.
519# behaviour = clutter.BehaviourPath(alpha,393 """
520# ((x,y),394 if (x, y) == (self._cursor_x, self._cursor_y):
521# (x,y - y_offset)))395 return
522# elif direction == self.DOWN:396
523# behaviour = clutter.BehaviourPath(alpha,397 self._cursor_behaviour.clear()
524# ((x,y),398
525# (x,y + y_offset)))399 start_knot = (self._cursor_x * self._item_width_abs + self._dx,
526# elif direction == self.RIGHT:400 self._cursor_y * self._item_height_abs + self._dy)
527# behaviour = clutter.BehaviourPath(alpha,401 stop_knot = (x * self._item_width_abs + self._dx,
528# ((x,y),402 y * self._item_height_abs + self._dy)
529# (x + x_offset,y)))403
530# elif direction == self.LEFT:404 self._cursor_behaviour.append_knots(start_knot, stop_knot)
531# behaviour = clutter.BehaviourPath(alpha,405
532# ((x,y),406 self._cursor_x, self._cursor_y = x, y
533# (x - x_offset,y)))407 self._cursor_timeline.set_duration(duration)
534# behaviour.apply(self.cursor)408 self._cursor_timeline.start()
535# self.cursor_timeline.start()409
536# else:410 def up(self):
537 if direction == self.UP:411 """Move up."""
538 self.cursor.move_by(0, -y_offset)412 if not self.on_top:
539 elif direction == self.DOWN:413 if self._is_vertical:
540 self.cursor.move_by(0, y_offset)414 self.selected_index -= self.items_per_row
541 elif direction == self.LEFT:415 else:
542 self.cursor.move_by(-x_offset, 0)416 self.selected_index -= 1
543 elif direction == self.RIGHT:417
544 self.cursor.move_by(x_offset, 0)418 def down(self):
545419 """Move down."""
546 def _move_content(self, count, direction):420 if not self.on_bottom:
547 """421 if self._is_vertical:
548 Move menuitems to given direction.422 self.selected_index += self.items_per_row
549 @param count: How much we scroll (number of menuitems)423 else:
550 @param direction: Which direction we scroll (See class constants)424 self.selected_index += 1
551 """425
552 # Change logical position426 def right(self):
553 x, y = self.content_position427 """Move right."""
554 if direction == self.UP:428 if not self.on_right:
555 self.content_position = (x, y+1)429 if self._is_vertical:
556 elif direction == self.DOWN:430 self.selected_index += 1
557 self.content_position = (x, y-1)431 else:
558 elif direction == self.LEFT:432 self.selected_index += self.items_per_col
559 self.content_position = (x+1, y)433
560 elif direction == self.RIGHT:434 def left(self):
561 self.content_position = (x-1, y)435 """Move left."""
562436 if not self.on_left:
563 x_offset = count * (self.cell_width + self.cell_spacing)437 if self._is_vertical:
564 y_offset = count * (self.cell_height + self.cell_spacing)438 self.selected_index -= 1
565# if self.animate:439 else:
566# x = self.itemgroup.get_x()440 self.selected_index -= self.items_per_col
567# y = self.itemgroup.get_y()441
568#442 def _internal_timer_callback(self):
569# # Finish previous animation before new443 """
570# if self.content_timeline is not None and (444 This callback is used to move the cursor if the SEEK mode is activated.
571# self.content_timeline.is_playing():445 """
572# self.content_timeline.pause()446 if self._event_mode == self.MODE_SEEK:
573# self.content_timeline.advance(4)447 if self._seek_step_x == 1:
574#448 self.right()
575# self.content_timeline = clutter.Timeline(4, 26)449 if self._seek_step_x == -1:
576# alpha = clutter.Alpha(self.content_timeline,450 self.left()
577# clutter.ramp_inc_func)451 if self._seek_step_y == 1:
578# if direction == self.UP:452 self.down()
579# behaviour = clutter.BehaviourPath(alpha,453 if self._seek_step_y == -1:
580# ((x,y),454 self.up()
581# (x,y - y_offset)))455
582# elif direction == self.DOWN:456 return True
583# behaviour = clutter.BehaviourPath(alpha,457
584# ((x,y),458 def _on_button_press_event(self, actor, event):
585# (x,y + y_offset)))459 """button-press-event handler."""
586# elif direction == self.RIGHT:460 clutter.grab_pointer(self._event_rect)
587# behaviour = clutter.BehaviourPath(alpha,461 if not self._event_rect.handler_is_connected(self._motion_handler):
588# ((x,y),462 self._motion_handler = self._event_rect.connect('motion-event',
589# (x + x_offset,y)))463 self._on_motion_event)
590# elif direction == self.LEFT:464
591# behaviour = clutter.BehaviourPath(alpha,465 (x_menu, y_menu) = self.get_transformed_position()
592# ((x,y),466 (x_moving_group, y_moving_group) = self._moving_group.get_position()
593# (x - x_offset,y)))467
594# behaviour.apply(self.itemgroup)468 # Events coordinates are relative to the stage.
595# self.content_timeline.start()469 # So they need to be computed relatively to the moving group.
596# else:470 x = event.x - x_menu - x_moving_group
597 if direction == self.UP:471 y = event.y - y_menu - y_moving_group
598 self.itemgroup.move_by(0, -y_offset)472
599 elif direction == self.DOWN:473 x_grid = int(x / self._item_width_abs)
600 self.itemgroup.move_by(0, y_offset)474 y_grid = int(y / self._item_height_abs)
601 elif direction == self.RIGHT:475
602 self.itemgroup.move_by(x_offset, 0)476 if self._is_vertical:
603 elif direction == self.LEFT:477 new_index = y_grid * self.items_per_row + x_grid
604 self.itemgroup.move_by(-x_offset, 0)478 else:
605479 new_index = x_grid * self.items_per_col + y_grid
606 def _scale_menuitem(self, menuitem):480
607 """481 (delta_x, delta_y) = self._index_to_xy(self._selected_index)
608 Scale menuitem to fit into one cell. Preserves actor's aspect ratio.482
609 @param menuitem: clutter.Actor483 delta_x -= x_grid
610 """484 delta_y -= y_grid
611 w, h = menuitem.get_size()485
612486 # Correction factor due to the fact that items are not necessary square,
613 if w == self.cell_width and h == self.cell_height:487 # but most probably rectangles. So the distance in the grid coordinates
614 return # Already correct size, no scale needed488 # must be corrected by a factor to have a real distance in pixels on the
615489 # screen.
616 x_ratio = self.cell_width / float(w)490 correction = float(self._item_width_abs) / float(self._item_height_abs)
617 y_ratio = self.cell_height / float(h)491 correction *= correction
618 if x_ratio > y_ratio:492 distance = math.sqrt(delta_x ** 2 * correction + delta_y ** 2)
619 menuitem.set_scale(self.cell_height /float(h),493
620 self.cell_height / float(h))494 # Computation of the duration of animations, scaling pixels to ms.
621 else:495 duration = int(distance * 50)
622 menuitem.set_scale(self.cell_width / float(w),496
623 self.cell_width / float(w))497 if self.selected_index == new_index and \
624498 self.active and \
625 def _scale_cursor(self):499 not self._cursor_timeline.is_playing() and \
626 """500 not self._moving_group_timeline.is_playing():
627 Scale cursor. Override this method if you want to use cursor that is501 self._event_mode = self.MODE_SELECT
628 smaller or larger than menuitem. This default implementation scales502 else:
629 cursor into the size of cell of the grid. Aspect ratio is not503 self.active = True
630 preserved!504 self._event_mode = self.MODE_NONE
631 """505
632 x_ratio = self.cell_width / float(self.cursor.get_width())506 self._set_selected_index(new_index, duration)
633 y_ratio = self.cell_height / float(self.cursor.get_height())507
634 self.cursor.set_scale(x_ratio, y_ratio)508 self._motion_buffer.start(event)
635509
636 def _update_menuitem_position(self, menuitem, logical_position):510 return False
637 """511
638 Recalculate menuitem's position.512 def _on_button_release_event(self, actor, event):
639 @param menuitem: Menuitem that is repositioned (clutter.Actor)513 """button-release-event handler."""
640 @param logical_position: Logical position of the menuitem514 clutter.ungrab_pointer()
641 """515 if self._event_rect.handler_is_connected(self._motion_handler):
642 if logical_position == 0:516 self._event_rect.disconnect_by_func(self._on_motion_event)
643 x = 0517
644 y = 0518 if self._event_mode == self.MODE_SELECT:
645 else:519 self.emit('selected')
646 row = (logical_position / self.row_count) # Item's row520
647 col = logical_position % self.row_count # Item's column521 self._event_mode = self.MODE_NONE
648 if self.orientation == self.VERTICAL:522
649 x = col * (self.cell_width + self.cell_spacing)523 return True
650 y = row * (self.cell_height + self.cell_spacing)524
651 elif self.orientation == self.HORIZONTAL:525 def _on_motion_event(self, actor, event):
652 y = col * (self.cell_height + self.cell_spacing)526 """motion-event handler"""
653 x = row * (self.cell_width + self.cell_spacing)527 # threshold in pixels = the minimum distance we have to move before we
654 menuitem.set_position(x, y)528 # consider a motion has started
655529 motion_threshold = 20
656 def _update_selected_item(self, new_location, old_location=None):530
657 """531 self._seek_step_x = 0
658 This method allows to modify currently selected item. This can be532 self._seek_step_y = 0
659 overriden in inherited widgets.533 self._motion_buffer.compute_from_start(event)
660 @param new_location: Logical location of current menuitem534 self._motion_buffer.compute_from_last_motion_event(event)
661 @param old_location: Logical location of last menuitem535
662 """536 if self._motion_buffer.distance_from_start > motion_threshold:
663 pass537 self._event_mode = self.MODE_SEEK
664538 self._motion_buffer.take_new_motion_event(event)
665 def _animate_items_on_state_change(self):539 dx = self._motion_buffer.dx_from_last_motion_event
666 """540 dy = self._motion_buffer.dy_from_last_motion_event
667 This method is called when widget is set active or inactive. The method541
668 allows widget to react this action. Method should be overriden in542 if math.fabs(dx) > math.fabs(dy):
669 inheriting classes. Default implementation doesn't do anything.543 self._seek_step_x = dx > 0 and 1 or -1
670 """544 else:
671 pass545 self._seek_step_y = dy > 0 and 1 or -1
546
547 return False
548
549 def _on_scroll_event(self, actor, event):
550 """scroll-event handler (mouse's wheel)."""
551 if not self.active:
552 self.active = True
553 return
554
555 if event.direction == clutter.SCROLL_DOWN:
556 self.down()
557 else:
558 self.up()
559
560 return False
672561
673562
=== modified file 'entertainerlib/frontend/gui/widgets/image_menu.py'
--- entertainerlib/frontend/gui/widgets/image_menu.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/frontend/gui/widgets/image_menu.py 2009-07-13 18:31:36 +0000
@@ -2,88 +2,160 @@
2'''Implements a grid menu that contains images'''2'''Implements a grid menu that contains images'''
33
4import clutter4import clutter
5import gtk
6import gobject
57
6from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu8from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu
7from entertainerlib.utils.configuration import Configuration9from entertainerlib.frontend.gui.widgets.menu_item import MenuItem
10from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture
11from entertainerlib.frontend.gui.widgets.texture import Texture
12
13class ImageMenuItem(MenuItem):
14 """A menuitem widget that contains a Texture."""
15
16 def __init__(self, width, height, texture):
17 MenuItem.__init__(self)
18
19 item_width = self.get_abs_x(width)
20 item_height = self.get_abs_y(height)
21
22 self.original_ratio = float(texture.get_width())
23 self.original_ratio /= texture.get_height()
24 item_ratio = float(item_width) / float(item_height)
25
26 margin = 0.95
27
28 if item_ratio >= self.original_ratio:
29 texture_width = self.original_ratio * item_height
30 texture_height = item_height
31 texture_width *= margin
32 texture_height *= margin
33 delta_width = item_width - texture_width
34 delta_height = item_height - texture_height
35 else:
36 texture_width = item_width
37 texture_height = item_width / self.original_ratio
38 texture_width *= margin
39 texture_height *= margin
40 delta_width = item_width - texture_width
41 delta_height = item_height - texture_height
42
43 item_x = delta_width / 2.0
44 item_y = delta_height / 2.0
45
46 texture.set_position(int(item_x), int(item_y))
47 texture.set_size(int(texture_width), int(texture_height))
48
49 self.add(texture)
50
851
9class ImageMenu(GridMenu):52class ImageMenu(GridMenu):
10 """53 """A grid menu that contains images."""
11 ImageMenu widget54 __gsignals__ = {
1255 'filled' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
13 This widget inherits genral GridMenu widget and implements simple56 }
14 imagemenu.57
15 """58 def __init__(self, x=0, y=0, item_width=200, item_height=100):
1659 GridMenu.__init__(self, x, y, item_width, item_height)
17 def __init__(self, theme):60
18 """Initialize widget"""61 self.cursor_below = False
19 GridMenu.__init__(self, self.HORIZONTAL)62 self.horizontal = True
2063 self.items_per_col = 4
21 #XXX: Joshua: This is to make sure the spaces in an image menu are the64 self.visible_rows = 3
22 #right size when the screen is not the default size of 1366x76865 self.visible_cols = 5
23 #This should eventually be replaced by proportional resizing.
24 config = Configuration()
25
26 width_ratio = config.get_stage_width() / 1366.0
27 height_ratio = config.get_stage_height() / 768.0
28
29 self.set_item_size(int(200 * width_ratio), int(150 * height_ratio))
30
31 #End of temporary code
32
33 self.set_row_count(3)
3466
35 c = clutter.Rectangle()67 c = clutter.Rectangle()
68 c.set_size(100, 100)
36 c.set_color((255, 255, 255, 128))69 c.set_color((255, 255, 255, 128))
37 self.set_cursor(c)70 self.cursor = c
3871
39 def set_animate(self, boolean):72 pix_buffer = gtk.gdk.pixbuf_new_from_file(
40 """73 self.config.theme.getImage("default_movie_art"))
41 Enable/disable widget animation.74 self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
42 @param boolean: True if widget should be animated, otherwise False75
43 """76 self.album_default = Texture(
44 self.animate = boolean77 self.config.theme.getImage("default_album_art"))
45 #self.cursor.animate(boolean)78
4679 def add_item(self, texture, data):
47 def _update_selected_item(self, new_location, old_location=None):80 """Add a ImageMenuItem from a Texture."""
48 """81 item = ImageMenuItem(self._item_width, self._item_height, texture)
49 Overridden method! See GridMenu API reference.82 item.userdata = data
50 @param new_location: Logical location of current menuitem83
51 @param old_location: Logical location of last menuitem84 self.raw_add_item(item)
52 """85
53 if old_location is not None:86 def async_add(self, items):
54 self.items[old_location].set_active(False)87 """Add asynchronously ImageMenuItem using a list."""
55 self.items[new_location].set_active(True)88 if len(items) > 0:
5689 item = items[0]
57 def _animate_items_on_state_change(self):90 self.add_item(item[0], item[1])
58 """91
59 This method is called when widget is set active or inactive. This92 # Recursive call, remove first element from the list
60 method allows widget to react this action.93 gobject.timeout_add(15, self.async_add, items[1:])
61 """94 else:
62 if len(self.items):95 self.emit("filled")
63 if self.active:96
64 self.cursor.show()97 return False
65 self.items[self.logical_position].set_active(True)98
66 self.set_opacity(255)99 def async_add_videos(self, items):
67 else:100 """
68 self.cursor.hide()101 Add asynchronously ImageMenuItem using a list.
69 self.items[self.logical_position].set_active(False)102 The created ImageMenuItem fits movies and series requirements.
70 self.set_opacity(128)103 """
71104 if len(items) > 0:
72 def _scale_menuitem(self, menuitem):105 item = items[0]
73 """106 if item[1].has_cover_art():
74 Scale menuitem to fit into one cell. Preserves actor's aspect ratio.107 pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
75 @param menuitem: clutter.Actor108 texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer)
76 """109 else:
77# w, h = menuitem.get_size()110 texture = clutter.CloneTexture(self.movie_default)
78#111
79# if w == self.cell_width and h == self.cell_height:112 self.add_item(texture, item[1])
80# return # Already correct size, no scale needed113
81#114 # Recursive call, remove first element from the list
82# x_ratio = self.cell_width / float(w)115 gobject.timeout_add(10, self.async_add_videos, items[1:])
83# y_ratio = self.cell_height / float(h)116 else:
84# if x_ratio > y_ratio:117 self.emit("filled")
85# menuitem.set_scale(self.cell_height /float(h),118
86# self.cell_height / float(h))119 return False
87# else:120
88# menuitem.set_scale(self.cell_width / float(w),121 def async_add_albums(self, items):
89# self.cell_width / float(w))122 """
123 Add asynchronously ImageMenuItem using a list.
124 The created ImageMenuItem fits albums requirements.
125 """
126 if len(items) > 0:
127 item = items[0]
128 if item[1].has_album_art():
129 texture = Texture(item[0])
130 else:
131 texture = clutter.CloneTexture(self.album_default)
132
133 self.add_item(texture, item[1])
134
135 # Recursive call, remove first element from the list
136 gobject.timeout_add(10, self.async_add_albums, items[1:])
137 else:
138 self.emit("filled")
139
140 return False
141
142 def async_add_clips(self, items):
143 """
144 Add asynchronously ImageMenuItem using a list.
145 The created ImageMenuItem fits clips requirements.
146 """
147 if len(items) > 0:
148 item = items[0]
149 pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0])
150 texture = RoundedTexture(0.0, 0.0, 0.23, self.y_for_x(0.23) * 0.7,
151 pix_buffer)
152
153 self.add_item(texture, item[1])
154
155 # Recursive call, remove first element from the list
156 gobject.timeout_add(10, self.async_add_clips, items[1:])
157 else:
158 self.emit("filled")
159
160 return False
161
90162
=== removed file 'entertainerlib/frontend/gui/widgets/image_menu_item.py'
--- entertainerlib/frontend/gui/widgets/image_menu_item.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/frontend/gui/widgets/image_menu_item.py 1970-01-01 00:00:00 +0000
@@ -1,34 +0,0 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Menuitem that contains an image'''
3
4from entertainerlib.frontend.gui.widgets.menu_item import MenuItem
5
6class ImageMenuItem(MenuItem):
7 """
8 Simple Image MenuItem
9
10 This menu item contains one Texture that is scaled in such a way that the
11 texture fills the whole menu item area. Texture aspect ratio is not
12 preserved. ImageMenuItem is defined in terms of its width with a ratio
13 determining the height relative to width. This is so the image will scale
14 predictably on monitors of different aspect ratios.
15 """
16
17 def __init__(self, x_percent, y_to_x_ratio, texture):
18 """Initialize menuitem"""
19
20 MenuItem.__init__(self)
21
22 width = self.get_abs_x(x_percent)
23 height = self._calculate_height(width, y_to_x_ratio)
24
25 texture.set_size(width, height)
26
27 self.add(texture)
28
29 def _calculate_height(self, width, y_to_x_ratio):
30 """Calculate pixel height from the ratio of y to x"""
31 height = int(width * y_to_x_ratio)
32
33 return height
34
350
=== modified file 'entertainerlib/frontend/gui/widgets/label.py'
--- entertainerlib/frontend/gui/widgets/label.py 2009-04-28 22:30:06 +0000
+++ entertainerlib/frontend/gui/widgets/label.py 2009-05-31 15:00:01 +0000
@@ -22,7 +22,7 @@
22 self._font_size = font_size22 self._font_size = font_size
23 self._set_font_size(font_size)23 self._set_font_size(font_size)
2424
25 self.set_color(self.theme.get_color(color_name))25 self.color = color_name
2626
27 self._position = None27 self._position = None
28 self._set_position((x_pos_percent, y_pos_percent))28 self._set_position((x_pos_percent, y_pos_percent))
@@ -32,33 +32,33 @@
32 if name:32 if name:
33 self.set_name(name)33 self.set_name(name)
3434
35 self._width = self._init_width()
36 self._height = self._init_height()
37
38 self.set_line_wrap(True)35 self.set_line_wrap(True)
3936
37 def _get_color(self):
38 """color property getter."""
39 return self._color
40
41 def _set_color(self, color_name):
42 """color property setter."""
43 self._color = color_name
44 self.set_color(self.theme.get_color(self._color))
45
46 color = property(_get_color, _set_color)
47
40 def set_size(self, x_percent, y_percent):48 def set_size(self, x_percent, y_percent):
41 """Override clutter label set_size to calculate absolute size from a49 """Override clutter label set_size to calculate absolute size from a
42 percentage"""50 percentage"""
43 self._width = x_percent
44 self._height = y_percent
45 clutter.Label.set_size(self,51 clutter.Label.set_size(self,
46 self.get_abs_x(x_percent),52 self.get_abs_x(x_percent),
47 self.get_abs_y(y_percent))53 self.get_abs_y(y_percent))
4854
49 def _init_width(self):
50 """Generate the width of the label initially before the width property
51 is set to ensure width has a value"""
52 return float(self.get_width()) / self.config.get_stage_width()
53
54 def _get_width(self):55 def _get_width(self):
55 """Return the width as a percent of the stage size"""56 """Return the width as a percent of the stage size"""
56 return self._width57 return float(self.get_width()) / self.config.get_stage_width()
5758
58 def _set_width(self, x_percent):59 def _set_width(self, x_percent):
59 """Set the width based on a hozirontal percent of the stage size.60 """Set the width based on a hozirontal percent of the stage size.
60 Provide clutter label set_width the absolute size from the percentage"""61 Provide clutter label set_width the absolute size from the percentage"""
61 self._width = x_percent
62 clutter.Label.set_width(self, self.get_abs_x(x_percent))62 clutter.Label.set_width(self, self.get_abs_x(x_percent))
6363
64 width = property(_get_width, _set_width)64 width = property(_get_width, _set_width)
@@ -80,20 +80,14 @@
8080
81 position = property(_get_position, _set_position)81 position = property(_get_position, _set_position)
8282
83 def _init_height(self):
84 """Generate the height of the label initially before the height property
85 is set to ensure height has a value"""
86 return float(self.get_height()) / self.config.get_stage_height()
87
88 def _get_height(self):83 def _get_height(self):
89 """Return the height as a percent of the stage size"""84 """Return the height as a percent of the stage size"""
90 return self._height85 return float(self.get_height()) / self.config.get_stage_height()
9186
92 def _set_height(self, y_percent):87 def _set_height(self, y_percent):
93 """Set the height based on a vertical percent of the stage size.88 """Set the height based on a vertical percent of the stage size.
94 Provide clutter label set_height the absolute size from the percentage89 Provide clutter label set_height the absolute size from the percentage
95 """90 """
96 self._height = y_percent
97 clutter.Label.set_height(self, self.get_abs_y(y_percent))91 clutter.Label.set_height(self, self.get_abs_y(y_percent))
9892
99 height = property(_get_height, _set_height)93 height = property(_get_height, _set_height)
10094
=== removed file 'entertainerlib/frontend/gui/widgets/menu.py'
--- entertainerlib/frontend/gui/widgets/menu.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/frontend/gui/widgets/menu.py 1970-01-01 00:00:00 +0000
@@ -1,252 +0,0 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Menu has a list of menuitems and it allows user to select one of them'''
3
4import clutter
5
6class Menu(clutter.Group):
7 """
8 Menu widget.
9
10 Blaa blaa...
11 """
12
13 def __init__(self):
14 """
15 Initialize menu widget.
16 """
17 clutter.Group.__init__(self)
18 self.__name = None # Menu name
19 self.__active = False # Is this menu active
20 self.__page_zize = 8 # How many items are displayed at the same
21 # time
22 self.__loop = False # Should menu loop from last to first
23 self.__selector = None # Selector widget
24 self.__items = [] # List of MenuItems
25 self.__current = 0 # Current position of the menu
26 self.__item_width = 0 # Menuitem width
27 self.__item_height = 0 # Menuitem height
28
29 def is_active(self):
30 """
31 Is this menu active.
32 @return boolean, True if menu is currently active, otherwise False
33 """
34 return self.__active
35
36 def set_active(self, boolean):
37 """
38 Set this widget active or inactive
39 @param boolean: True to set widget active, False to set inactive
40 """
41 self.__active = boolean
42 if boolean:
43 if self.__selector is not None:
44 self.__selector.show()
45 self.get_current_menuitem().set_active(True)
46 self.set_opacity(255)
47 else:
48 if self.__selector is not None:
49 self.__selector.hide()
50 self.get_current_menuitem().set_active(False)
51 self.set_opacity(128)
52
53 def add(self, menuitem):
54 """
55 Add new menuitem to the menu.
56 @param menuitem: Add this menuitem (Menitem Widget)
57 """
58 # First menuitem determines the size of all menuitems (is it good?)
59 if len(self.__items) == 0:
60 self.__item_width = menuitem.get_width()
61 self.__item_height = menuitem.get_height()
62
63 menuitem.set_position(0, len(self.__items) * menuitem.get_height())
64 self.__items.append(menuitem)
65 clutter.Group.add(self, menuitem)
66 self.update_clip()
67
68 def remove(self, menuitem):
69 """
70 Remove menuitem from the menu.
71 @param menuitem: Remove this menuitem (Menitem Widget)
72 """
73 clutter.Group.remove(self, menuitem)
74 for item in self.__items:
75 if item is menuitem:
76 del item
77
78 def remove_all(self):
79 """
80 Remove all menuitems from the menu.
81 @param menuitem: Remove this menuitem (Clutter.Actor)
82 """
83 clutter.Group.remove_all(self)
84 self.__current = 0
85 del self.__selector
86 self.__selector = None
87 del self.__items
88 self.__items = []
89
90 def set_loop(self, boolean):
91 """
92 Set loop for menu. If loop is true then first menuitem comes after the
93 last one.
94 @param boolean: True if menu should loop, otherwise False
95 """
96 self.__loop = boolean
97
98 def is_loop(self):
99 """
100 Is menu in loop mode.
101 @return: Boolean value
102 """
103 return self.__loop
104
105 def set_menuitem_background(self, texture):
106 """
107 Set texture that is displayed under every menuitem. This is optional.
108 @param texture: Menuitem background texture. (clutter.Actor)
109 """
110 for index in range(len(self.__items)):
111 bg = clutter.CloneTexture(texture)
112 bg.set_size(self.get_width(), self.__item_height)
113 bg.set_position(0, index * self.__item_height)
114 bg.set_opacity(128)
115 bg.set_name("background")
116 clutter.Group.add(self, bg)
117 for item in self.__items:
118 clutter.Group.lower(self, bg, item)
119
120 def set_selector(self, selector):
121 """
122 Set selector texture. Selector is moved over menuitems when user
123 browses the menu.
124 @param selector: Selector texture (Clutter.Actor)
125 """
126 selector.set_position(0, 0)
127 x_scale = self.__item_width / float(selector.get_width())
128 y_scale = self.__item_height / float(selector.get_height())
129 selector.set_size(self.__item_width, self.__item_height)
130 selector.set_scale(x_scale, y_scale)
131 selector.set_name("selector")
132 self.__selector = selector
133 clutter.Group.add(self, self.__selector)
134 for item in self.__items:
135 if item.get_name() != "background":
136 clutter.Group.lower(self, selector, item)
137
138 def scroll_up(self):
139 """
140 Scroll menu up (one menuitem).
141 """
142 if self.__current > 0:
143 self.__items[self.__current].set_active(False)
144 self.__current = self.__current - 1
145 self.__items[self.__current].set_active(True)
146 if self.__selector is not None:
147 self.__selector.set_position(0,
148 self.__current * self.__item_height)
149
150 # Loop from top to bottom
151 elif self.__current == 0 and self.__loop:
152 self.__items[self.__current].set_active(False)
153 self.__current = len(self.__items) - 1
154 self.__items[self.__current].set_active(True)
155 if self.__selector is not None:
156 self.__selector.set_position(0,
157 self.__current * self.__item_height)
158
159 def scroll_down(self):
160 """
161 Scroll menu down (one menuitem).
162 """
163 if self.__current < len(self.__items) - 1:
164 self.__items[self.__current].set_active(False)
165 self.__current = self.__current + 1
166 self.__items[self.__current].set_active(True)
167 if self.__selector is not None:
168 self.__selector.set_position(0,
169 self.__current * self.__item_height)
170
171 # Loop from bottom to top
172 elif self.__current == len(self.__items) - 1 and self.__loop:
173 self.__items[self.__current].set_active(False)
174 self.__current = 0
175 self.__items[self.__current].set_active(True)
176 if self.__selector is not None:
177 self.__selector.set_position(0,
178 self.__current * self.__item_height)
179
180 def scroll_up_page(self):
181 """
182 Scroll menu up one page. One page is a number of visible items.
183 """
184
185 def scroll_down_page(self):
186 """
187 Scroll menu down one page. One page is a number of visible items.
188 """
189 # Set current menuitem
190 self.__items[self.__current].set_active(False)
191 self.__current = self.__current + 8
192 self.__items[self.__current].set_active(True)
193
194 # Set selector position
195 if self.__selector is not None:
196 self.__selector.set_position(0,
197 self.__current * self.__items[0].get_height())
198
199 # Clip to current position of the menu
200 #self.set_clip(0,)
201
202 def update_clip(self):
203 """
204 Update menu clipping. This method decides which partion of menu is
205 actually displayed on screen. If you have a long menu, not all items
206 fit into the window. This function checks what is the current position
207 of the menulist and then clips the widget in such a way that current
208 position is on screen.
209 """
210 if len(self.__items) <= self.__page_zize:
211 self.remove_clip()
212 else:
213 self.set_clip(0,
214 self.__current * self.__item_height,
215 self.__item_width,
216 self.__item_height * self.__page_zize)
217 self.set_height(self.__item_height * self.__page_zize)
218
219 def get_current_position(self):
220 """
221 Get current position of the list. This position is under selector.
222 """
223 return self.__current
224
225 def get_current_menuitem(self):
226 """
227 Get Clutter.Actor object from the current postition.
228 """
229 if len(self.__items) == 0:
230 return None
231 return self.__items[self.__current]
232
233 def get_number_of_items(self):
234 """
235 Get number of menuitems in menu.
236 """
237 return len(self.__items)
238
239 def set_name(self, name):
240 """
241 Set menu name
242 @param name: Name of the menu (used to identify menu from others)
243 """
244 self.__name = name
245
246 def get_name(self):
247 """
248 Get name of the menu.
249 @return name as String
250 """
251 return self.__name
252
2530
=== modified file 'entertainerlib/frontend/gui/widgets/menu_item.py'
--- entertainerlib/frontend/gui/widgets/menu_item.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/frontend/gui/widgets/menu_item.py 2009-05-28 19:28:56 +0000
@@ -1,51 +1,34 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Simple Menuitem'''2"""Simple Menuitem."""
33
4import clutter4import clutter
55
6from entertainerlib.frontend.gui.widgets.base import Base6from entertainerlib.frontend.gui.widgets.base import Base
77
8class MenuItem(Base, clutter.Group):8class MenuItem(Base, clutter.Group):
9 """9 """Simple menuitem widget."""
10 Simple menuitem widget.
11
12 This is a base class for all menuitem types. MenuItems are supposed to be
13 used in Menus which inherit GridMenu class.
14 """
1510
16 def __init__(self):11 def __init__(self):
17 """Initialize menuitem."""
18
19 Base.__init__(self)12 Base.__init__(self)
20 clutter.Group.__init__(self)13 clutter.Group.__init__(self)
21 self.active = False14
22 self.data = None15 self._userdata = None
2316
24 def set_active(self, boolean):17 def _get_userdata(self):
25 """18 """userdata property getter."""
26 Set menuitem active / inactive.19 return self._userdata
27 @param boolean: use True to set active and False to set inactive20
28 """21 def _set_userdata(self, userdata):
29 self.active = boolean22 """userdata property getter."""
3023 self._userdata = userdata
31 def is_active(self):24
32 """25 userdata = property (_get_userdata, _set_userdata)
33 Get widget status.26
34 @return True if widget is active, otherwise False27 def animate_in(self):
35 """28 """Animation to be done when an item gets selected."""
36 return self.active29 pass
3730
38 def set_userdata(self, data):31 def animate_out(self):
39 """32 """Animation to be done when an item gets unselected."""
40 Set user data for this menuitem.33 pass
41 @param id: ID string
42 """
43 self.data = data
44
45 def get_userdata(self):
46 """
47 Get user data
48 @return: string
49 """
50 return self.data
5134
5235
=== modified file 'entertainerlib/frontend/gui/widgets/scroll_menu.py'
--- entertainerlib/frontend/gui/widgets/scroll_menu.py 2009-05-09 17:03:51 +0000
+++ entertainerlib/frontend/gui/widgets/scroll_menu.py 2009-05-19 11:47:09 +0000
@@ -252,7 +252,6 @@
252 if not self.handler_is_connected(self._motion_handler):252 if not self.handler_is_connected(self._motion_handler):
253 self._motion_handler = self.connect('motion-event',253 self._motion_handler = self.connect('motion-event',
254 self._on_motion_event)254 self._on_motion_event)
255 self.active = True
256255
257 if self._timeline.is_playing():256 if self._timeline.is_playing():
258 # before we stop the timeline, store its progression257 # before we stop the timeline, store its progression
@@ -281,6 +280,10 @@
281 self.disconnect_by_func(self._on_motion_event)280 self.disconnect_by_func(self._on_motion_event)
282 self._motion_buffer.compute_from_last_motion_event(event)281 self._motion_buffer.compute_from_last_motion_event(event)
283282
283 if not self.active:
284 self.active = True
285 return
286
284 y = event.y - self.get_y()287 y = event.y - self.get_y()
285288
286 if self._event_mode == self.MODE_SELECTION:289 if self._event_mode == self.MODE_SELECTION:
287290
=== modified file 'entertainerlib/frontend/gui/widgets/selector.py'
--- entertainerlib/frontend/gui/widgets/selector.py 2009-05-06 03:40:22 +0000
+++ entertainerlib/frontend/gui/widgets/selector.py 2009-05-19 18:57:18 +0000
@@ -17,7 +17,7 @@
17 SMALL = 017 SMALL = 0
18 LARGE = 118 LARGE = 1
1919
20 def __init__(self, size, theme):20 def __init__(self, theme):
21 """21 """
22 Initialize Selector widget and start animation.22 Initialize Selector widget and start animation.
23 @param size: Size of the selector. Use class constants SMALL and LARGE23 @param size: Size of the selector. Use class constants SMALL and LARGE
@@ -26,19 +26,8 @@
26 clutter.Group.__init__(self)26 clutter.Group.__init__(self)
27 self.animate_selector = False27 self.animate_selector = False
2828
29 # Get proper file names29 selector_filename = theme.getImage("selector")
30 # XXX Samuel Buffet : This maybe should be considered as technical debt30 glow_filename = theme.getImage("selector_glow")
31 # because neither selector_small nor selector_glow_small
32 # are provided by Themes
33 if size == self.SMALL:
34 selector_filename = theme.getImage("selector_small")
35 glow_filename = theme.getImage("selector_glow_small")
36 elif size == self.LARGE:
37 selector_filename = theme.getImage("selector")
38 glow_filename = theme.getImage("selector_glow")
39 else:
40 raise Exception(
41 "Intializing Selector object failed. Unknown size!")
4231
43 # Set selector base texture32 # Set selector base texture
44 self.selector = Texture(selector_filename)33 self.selector = Texture(selector_filename)
@@ -57,13 +46,10 @@
57 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)46 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)
58 self.behaviour.apply(self.glow)47 self.behaviour.apply(self.glow)
5948
60 def animate(self, boolean):49 def set_size(self, width, height):
61 """50 """Overrides clutter.Actor.set_size()"""
62 Animate widget when it's on screen.51 self.selector.set_size(width, height)
63 @param boolean: True if widget should be animated, otherwise False52 self.glow.set_size(width, height)
64 """
65 self.animate_selector = boolean
66 #self.stop_animation()
6753
68 def stop_animation(self):54 def stop_animation(self):
69 """Stop selector animation"""55 """Stop selector animation"""
@@ -83,18 +69,15 @@
83 def show(self):69 def show(self):
84 """Overrides clutter.Actor.show()"""70 """Overrides clutter.Actor.show()"""
85 clutter.Group.show(self)71 clutter.Group.show(self)
86 if self.animate_selector:72 self.start_animation()
87 self.start_animation()
8873
89 def hide_all(self):74 def hide_all(self):
90 """Overrides clutter.Actor.hide_all()"""75 """Overrides clutter.Actor.hide_all()"""
91 clutter.Group.hide_all(self)76 clutter.Group.hide_all(self)
92 if self.animate_selector:77 self.stop_animation()
93 self.stop_animation()
9478
95 def show_all(self):79 def show_all(self):
96 """Overrides clutter.Actor.show_all()"""80 """Overrides clutter.Actor.show_all()"""
97 clutter.Group.show_all(self)81 clutter.Group.show_all(self)
98 if self.animate_selector:82 self.start_animation()
99 self.start_animation()
10083
10184
=== modified file 'entertainerlib/frontend/gui/widgets/special_behaviours.py'
--- entertainerlib/frontend/gui/widgets/special_behaviours.py 2009-05-09 17:03:51 +0000
+++ entertainerlib/frontend/gui/widgets/special_behaviours.py 2009-05-28 19:20:31 +0000
@@ -51,3 +51,23 @@
51 dy = self.end_knot[1] - self.start_knot[1]51 dy = self.end_knot[1] - self.start_knot[1]
52 return math.sqrt(dx * dx + dy * dy)52 return math.sqrt(dx * dx + dy * dy)
5353
54class FontSizeBehaviour(clutter.Behaviour):
55 """A custom Behaviour to change the `font-size` property of Labels."""
56 __gtype_name__ = 'FontSizeBehaviour'
57
58 def __init__(self, alpha=None):
59 clutter.Behaviour.__init__(self)
60 self.set_alpha(alpha)
61 self.start_size = 1.0
62 self.end_size = 1.0
63
64 def do_alpha_notify(self, alpha_value):
65 """Alpha function changing the `font-size` property of Labels."""
66 size = alpha_value * (self.end_size - self.start_size)
67 size /= clutter.MAX_ALPHA
68 size += self.start_size
69
70 for actor in self.get_actors():
71 actor.font_size = size
72 actor.update()
73
5474
=== modified file 'entertainerlib/frontend/gui/widgets/tab_group.py'
--- entertainerlib/frontend/gui/widgets/tab_group.py 2009-05-12 17:18:10 +0000
+++ entertainerlib/frontend/gui/widgets/tab_group.py 2009-05-21 20:23:26 +0000
@@ -193,13 +193,12 @@
193193
194 def _on_tab_title_button_press(self, actor, event):194 def _on_tab_title_button_press(self, actor, event):
195 '''Title Label button-press handler.'''195 '''Title Label button-press handler.'''
196 if self.active:196 self.active = True
197 clicked_tab = self._tabs[actor.get_name()]197
198 clicked_tab_index = self.tabs_list.index(clicked_tab)198 clicked_tab = self._tabs[actor.get_name()]
199199 clicked_tab_index = self.tabs_list.index(clicked_tab)
200 if self.current_tab != clicked_tab_index:200
201 self._activate_tab(clicked_tab_index)201 if self.current_tab != clicked_tab_index:
202 self.current_tab = clicked_tab_index202 self._activate_tab(clicked_tab_index)
203 else:203 self.current_tab = clicked_tab_index
204 self.active = True
205204
206205
=== modified file 'entertainerlib/frontend/gui/widgets/text_menu.py'
--- entertainerlib/frontend/gui/widgets/text_menu.py 2009-05-06 02:58:08 +0000
+++ entertainerlib/frontend/gui/widgets/text_menu.py 2009-07-13 18:31:36 +0000
@@ -1,65 +1,176 @@
1# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv21# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2'''Implements basic menu that contains text labels as menuitems'''2'''Implements a grid menu that contains one or two labels.'''
33
4import clutter
5import gobject
6import pango
7
8from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu
9from entertainerlib.frontend.gui.widgets.label import Label
10from entertainerlib.frontend.gui.widgets.menu_item import MenuItem
4from entertainerlib.frontend.gui.widgets.selector import Selector11from entertainerlib.frontend.gui.widgets.selector import Selector
5from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu12from entertainerlib.frontend.gui.widgets.special_behaviours import (
13 FontSizeBehaviour)
14
15class TextMenuItem(MenuItem):
16 """A menuitem widget that contains one or two labels."""
17
18 def __init__(self, width, height, text, extra_text=None):
19 MenuItem.__init__(self)
20
21 self.width = width
22 self.height = height
23 self.theme = self.config.theme
24
25 self.text = text
26 self.extra_text = extra_text
27 self.color = "menuitem_inactive"
28 self.font_size = 0.03
29
30 self.label = Label(self.font_size, self.color, 0, 0, "",
31 "text_label")
32 self.add(self.label)
33
34 # Set extra text
35 self.extra_label = None
36 if extra_text is not None:
37 self.extra_label = Label(self.font_size, self.color, 0, 0,
38 "", "text_label")
39 self.add(self.extra_label)
40
41 self.update(text, extra_text)
42
43 self.timeline = clutter.Timeline(fps=100, duration=500)
44 alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)
45 self.behaviour = FontSizeBehaviour(alpha)
46 self.behaviour.apply(self)
47
48 def animate_in(self):
49 """Set labels font-size and color when an item gets selected."""
50 self.font_size = 0.037
51 self.color = "menuitem_active"
52 self.update()
53
54 def animate_out(self):
55 """Set labels font-size and color when an item gets unselected."""
56 self.font_size = 0.03
57 self.color = "menuitem_inactive"
58 self.update()
59
60 def update(self, text=None, extra_text=None):
61 """Updates text and dimensions of a TextMenuItem."""
62 if text is None:
63 text = self.text
64 else:
65 self.text = text
66
67 if extra_text is None:
68 extra_text = self.extra_text
69 else:
70 self.extra_text = extra_text
71
72 try:
73 first_line = text[:text.index('\n')]
74 except ValueError:
75 first_line = text
76
77 self.label.font_size = self.font_size
78 self.label.set_text(first_line)
79 self.label.position = (0.01, (self.height - self.label.height) / 2)
80 self.label.set_ellipsize(pango.ELLIPSIZE_END)
81
82 # Set extra text
83 if extra_text is not None:
84 self.extra_label.font_size = self.font_size
85 self.extra_label.set_text(extra_text)
86 self.extra_label.position = ( \
87 self.width * 0.98 - self.extra_label.width,
88 (self.height - self.extra_label.height) / 2)
89
90 self.label.width = (self.width - self.extra_label.width) * 0.9
91 else:
92 self.label.width = self.width * 0.95
93
94 self.label.color = self.color
95 if self.extra_label:
96 self.extra_label.color = self.color
97
98
99class ArtistsMenuItem(TextMenuItem):
100 """A TextMenuItem dedicated implementing animate_in and animate_out."""
101
102 def __init__(self, width, height, text, extra_text=None):
103 TextMenuItem.__init__(self, width, height, text, extra_text)
104
105 self.move_anchor_point_from_gravity(clutter.GRAVITY_WEST)
106 self.font_size = 0.025
107 self.update()
108
109 def animate_in(self):
110 """Set labels font-size and color when an item gets selected."""
111 self.timeline.stop()
112 self.font_size = 0.05
113 self.color = "menuitem_active"
114 self.update()
115
116 def animate_out(self):
117 """Set labels font-size and color when an item gets unselected."""
118 self.color = "menuitem_inactive"
119 self.update()
120 self.behaviour.start_size = 0.05
121 self.behaviour.end_size = 0.025
122 self.timeline.start()
123
6124
7class TextMenu(GridMenu):125class TextMenu(GridMenu):
8 """126 """A grid menu that contains labels."""
9 TextMenu widget127 __gsignals__ = {
10128 'filled' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
11 This widget inherits general GridMenu widget and implements simple textmenu.129 }
12 """130
13131 def __init__(self, x=0, y=0, item_width=200, item_height=100):
14 def __init__(self, theme, animate):132 GridMenu.__init__(self, x, y, item_width, item_height)
15 """133
16 Initialize widget134 self.items_per_row = 1
17135 self.visible_rows = 8
18 @param theme: Theme object136 self.visible_cols = 1
19 @param animate: Animate this menu widget (boolean)137
20 """138 self.cursor = Selector(self.config.theme)
21 GridMenu.__init__(self)139
22140 def add_item(self, text, extra_text=None, data=None):
23 self.animate = animate # Animate this widget behaviour141 """Add a TextMenuItem."""
24 self.set_item_size(570, 60) # Default size for textmenu item142 item = TextMenuItem(self._item_width, self._item_height, text,
25143 extra_text)
26 # Set Cursor144 item.userdata = data
27 self.set_cursor_placement(self.BELOW)145
28 c = Selector(Selector.LARGE, theme)146 self.raw_add_item(item)
29 c.animate(animate)147
30 self.set_cursor(c)148 def async_add(self, items):
31149 """Add asynchronously TextMenuItem using a list."""
32 def _update_selected_item(self, new_location, old_location=None):150 if len(items) > 0:
33 """151 item = items[0]
34 Overridden method! See GridMenu API reference.152 self.add_item(item[0], item[1], item[2])
35 @param new_location: Logical location of current menuitem153
36 @param old_location: Logical location of last menuitem154 # Recursive call, remove first element from the list
37 """155 gobject.timeout_add(10, self.async_add, items[1:])
38 if old_location is not None:156 else:
39 self.items[old_location].set_active(False)157 self.emit("filled")
40 self.items[new_location].set_active(True)158
41159 return False
42 def _animate_items_on_state_change(self):160
43 """161 def async_add_artists(self, items):
44 This method is called when widget is set active or inactive. This162 """Add asynchronously ArtistsMenuItem using a list."""
45 method allows widget to react this action.163 if len(items) > 0:
46 """164 item = items[0]
47 if len(self.items):
48 if self.active:
49 self.cursor.show()
50 self.items[self.logical_position].set_active(True)
51 self.set_opacity(255)
52 else:
53 self.cursor.hide()
54 self.items[self.logical_position].set_active(False)
55 self.set_opacity(128)
56
57 def get_index(self, text):
58 """
59 Returns index of label with the text as passed, returns -1 if not found
60 """
61 for item in self.items:
62 if item.label.get_text() == text:
63 return self.items.index(item)
64 return -1
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches