Merge lp:~samuel-buffet/entertainer/reactive_scrollarea into lp:entertainer
- reactive_scrollarea
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Matt Layman |
Approved revision: | 396 |
Merged at revision: | not available |
Proposed branch: | lp:~samuel-buffet/entertainer/reactive_scrollarea |
Merge into: | lp:entertainer |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~samuel-buffet/entertainer/reactive_scrollarea |
Related bugs: | |
Related blueprints: |
Touchscreen support
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Layman | Approve | ||
Review via email:
|
Commit message
The ScrollArea widget is now reactive to pointer events.
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuel Buffet (samuel-buffet) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Matt Layman (mblayman) wrote : | # |
Samuel,
Minimal comments on this one.
test_scrollarea.py:
* test_create is testing the label instance and not the scroll area.
scroll_area.py:
* _set_active: Is there much benefit to testing for "if self._offset_max >= 0"? It seems like that would only occur when the content hasn't been added yet. I'm just trying to understand what situation this would occur in.
* _update_
* _on_button_
Thanks!
- 396. By Samuel Buffet
-
Fixes after Matt's comments.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Samuel Buffet (samuel-buffet) wrote : | # |
Hi Matt,
> test_scrollarea.py:
> * test_create is testing the label instance and not the scroll area.
Yeah, a stupid mistake which is now fixed.
> scroll_area.py:
> * _set_active: Is there much benefit to testing for "if self._offset_max >=
> 0"? It seems like that would only occur when the content hasn't been added
> yet. I'm just trying to understand what situation this would occur in.
Yes there's a benefit because self._offset_max stands for :
self._offset_max = self.content.
In words it's the different between the height of the content and the height of the allowed on screen area. So if it is >=0 it means that the content is higher so there's a need to scroll something.
> * _update_
> don't really understand the comment on the line above. I'm not sure how to
> read "index = 1 => index = 0".
Yep, was not clear enough. The comment as been reworked.
> * _on_button_
> pixels?).
Unfortunately yes it's a bit magic. It is in ms because speed is in pixel / ms. This magic gives pretty good result but definitely a much more physics would be necessary providing a speed continuity in the motion when the button is released and a deceleration factor.
Thanks for the review Matt.
Samuel-
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... IOError: Couldn't read configuration file.
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [ERROR]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [ERROR]
testCreate ... [ERROR]
testGetCfgDir ... [ERROR]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [ERROR]
test_
test_sanitize ... [ERROR]
test_taint ... [ERROR]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [ERROR]
entertainerlib.
DatabaseTest
testCreate ... [ERROR]
testUseExisting ... [ERROR]
entertainerlib.
...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... IOError: Couldn't read configuration file.
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [ERROR]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [ERROR]
testCreate ... [ERROR]
testGetCfgDir ... [ERROR]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [ERROR]
test_
test_sanitize ... [ERROR]
test_taint ... [ERROR]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [ERROR]
entertainerlib.
DatabaseTest
testCreate ... [ERROR]
testUseExisting ... [ERROR]
entertainerlib.
...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... IOError: Couldn't read configuration file.
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [ERROR]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [ERROR]
testCreate ... [ERROR]
testGetCfgDir ... [ERROR]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [ERROR]
test_
test_sanitize ... [ERROR]
test_taint ... [ERROR]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [ERROR]
entertainerlib.
DatabaseTest
testCreate ... [ERROR]
testUseExisting ... [ERROR]
entertainerlib.
...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... IOError: Couldn't read configuration file.
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [ERROR]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [ERROR]
testCreate ... [ERROR]
testGetCfgDir ... [ERROR]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [ERROR]
test_
test_sanitize ... [ERROR]
test_taint ... [ERROR]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [ERROR]
entertainerlib.
DatabaseTest
testCreate ... [ERROR]
testUseExisting ... [ERROR]
entertainerlib.
...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... [OK]
test_create ... [OK]
entertainerlib.
BaseTest
test_create ... [OK]
test_get_abs_x ... [OK]
test_get_abs_y ... [OK]
test_y_for_x ... [OK]
entertainerlib.
ConfigurationTest
testBorg ... [OK]
testCreate ... [OK]
testGetCfgDir ... [OK]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [OK]
test_
test_sanitize ... [OK]
test_taint ... [OK]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [OK]
entertainerlib.
DatabaseTest
testCreate ... [OK]
testUseExisting ... [OK]
entertainerlib.
EyeCandyTextu
test_create ... [OK]
entertainerlib.
FeedConfigToo
testAddFeed
testAddFeed
testAddFile
testAddFile
entertainerlib.
FeedEntryPars
testConvert ... [OK]
testStripNo
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... [OK]
test_create ... [OK]
entertainerlib.
BaseTest
test_create ... [OK]
test_get_abs_x ... [OK]
test_get_abs_y ... [OK]
test_y_for_x ... [OK]
entertainerlib.
ConfigurationTest
testBorg ... [OK]
testCreate ... [OK]
testGetCfgDir ... [OK]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [OK]
test_
test_sanitize ... [OK]
test_taint ... [OK]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [OK]
entertainerlib.
DatabaseTest
testCreate ... [OK]
testUseExisting ... [OK]
entertainerlib.
EyeCandyTextu
test_create ... [OK]
entertainerlib.
FeedConfigToo
testAddFeed
testAddFeed
testAddFile
testAddFile
entertainerlib.
FeedEntryPars
testConvert ... [OK]
testStripNo
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... /home/rockstar/
/home/rockstar/
/home/rockstar/
/home/rockstar/
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [OK]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [OK]
testCreate ... [OK]
testGetCfgDir ... [OK]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [OK]
test_
test_sanitize ... [ERROR]
test_taint ... [OK]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [OK]
entertainerlib.
DatabaseTest
testCreate ... [OK]
testUseExisting ... [OK]
entertainerlib.
EyeCandyTextu
test_create ... ...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paul Hummer (rockstar) wrote : | # |
`which trial` entertainerlib.
entertainerlib.
ArrowTextureTest
test_bounce ... /home/rockstar/
/home/rockstar/
/home/rockstar/
/home/rockstar/
test_create ... [ERROR]
entertainerlib.
BaseTest
test_create ... [OK]
test_get_abs_x ... [ERROR]
test_get_abs_y ... [ERROR]
test_y_for_x ... [ERROR]
entertainerlib.
ConfigurationTest
testBorg ... [OK]
testCreate ... [OK]
testGetCfgDir ... [OK]
testGetSlid
testGetStag
testGetStag
testGetThem
testSetStag
testSetStag
testStartAu
testTrayIco
test_create_dir ... [OK]
test_
test_sanitize ... [ERROR]
test_taint ... [OK]
test_
test_
test_
entertainerlib.
ConnectionSer
testPortBinding ... [OK]
entertainerlib.
DatabaseTest
testCreate ... [OK]
testUseExisting ... [OK]
entertainerlib.
EyeCandyTextu
test_create ... ...
Preview Diff
1 | === modified file 'entertainerlib/frontend/gui/screens/album.py' |
2 | --- entertainerlib/frontend/gui/screens/album.py 2009-05-10 07:43:27 +0000 |
3 | +++ entertainerlib/frontend/gui/screens/album.py 2009-07-14 10:46:47 +0000 |
4 | @@ -11,7 +11,6 @@ |
5 | from entertainerlib.frontend.gui.widgets.label import Label |
6 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
7 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
8 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
9 | |
10 | class Album(Screen): |
11 | '''Screen that allows user to browse and play tracks of the music album.''' |
12 | @@ -28,21 +27,25 @@ |
13 | self.track_menu = None |
14 | |
15 | # Create and initialize screen items |
16 | - self._create_track_menu() |
17 | + self.track_menu = self._create_track_menu() |
18 | + self.add(self.track_menu) |
19 | self._create_album_cover_texture() |
20 | self._create_album_info() |
21 | |
22 | self.screen_title = Label(0.13, "screentitle", 0, 0.87, "") |
23 | self.screen_title.set_ellipsize(pango.ELLIPSIZE_END) |
24 | self.screen_title.width = 0.8 |
25 | - self.show_artist() |
26 | self.add(self.screen_title) |
27 | |
28 | #List indicator |
29 | - self.li = ListIndicator(0.74, 0.8, 0.2, 0.045, ListIndicator.VERTICAL) |
30 | - self.li.set_maximum(self.track_menu.get_number_of_items()) |
31 | + self.li = ListIndicator(0.74, 0.85, 0.2, 0.045, ListIndicator.VERTICAL) |
32 | + self.li.set_maximum(len(self.album.get_tracks())) |
33 | self.add(self.li) |
34 | |
35 | + self.track_menu.active = True |
36 | + self.track_menu.connect('selected', self._on_menu_selected) |
37 | + self.track_menu.connect('moved', self._display_selected_track) |
38 | + |
39 | def _create_album_cover_texture(self): |
40 | """ |
41 | Create a texture that is displayed next to track list. This texture |
42 | @@ -83,31 +86,14 @@ |
43 | """ |
44 | Create track menu. This menu contains list of all tracks on album. |
45 | """ |
46 | - self.track_menu = TextMenu(self.theme, self.config.show_effects()) |
47 | - self.track_menu.set_row_count(1) |
48 | - self.track_menu.set_visible_column_count(7) |
49 | - self.track_menu.set_item_size(self.get_abs_x(0.4393), |
50 | - self.get_abs_y(0.0781)) |
51 | - self.track_menu.set_position(self.get_abs_x(0.4978), |
52 | - self.get_abs_y(0.2344)) |
53 | + menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781) |
54 | |
55 | - # Create menu items based on MusicLibrary |
56 | tracks = self.album.get_tracks() |
57 | - for track in tracks: |
58 | - track_length = str(track.get_length() / 60) + ":" + \ |
59 | - str(track.get_length() % 60).zfill(2) |
60 | - item = TextMenuItem(0.4393, 0.0781, track.get_title(), track_length) |
61 | - item.set_userdata(track) |
62 | - self.track_menu.add_actor(item) |
63 | - |
64 | - self.track_menu.set_active(True) |
65 | - self.add(self.track_menu) |
66 | - |
67 | - def show_artist(self): |
68 | - # Screen Title (Displayed at the bottom left corner) |
69 | - track = self.track_menu.get_current_menuitem().get_userdata() |
70 | - self.screen_title.set_text(track.get_artist()) |
71 | - self.screen_title.show() |
72 | + tracks_list = [[track.get_title(), track.get_length_string(), track] \ |
73 | + for track in tracks] |
74 | + menu.async_add(tracks_list) |
75 | + |
76 | + return menu |
77 | |
78 | def is_interested_in_play_action(self): |
79 | """ |
80 | @@ -121,27 +107,32 @@ |
81 | Override function from Screen class. See Screen class for |
82 | better documentation. |
83 | """ |
84 | - track = self.track_menu.get_current_menuitem().get_userdata() |
85 | + track = self.track_menu.selected_userdata |
86 | self.media_player.set_media(track) |
87 | self.media_player.play() |
88 | |
89 | - def _move_menu(self, menu_direction): |
90 | - '''Move the text menu in the provided direction.''' |
91 | - self.track_menu.move(menu_direction) |
92 | - self.li.set_current(self.track_menu.get_current_position() + 1) |
93 | - self.show_artist() |
94 | - |
95 | def _handle_up(self): |
96 | '''Handle UserEvent.NAVIGATE_UP.''' |
97 | - self._move_menu(TextMenu.UP) |
98 | + self.track_menu.up() |
99 | |
100 | def _handle_down(self): |
101 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
102 | - self._move_menu(TextMenu.DOWN) |
103 | + self.track_menu.down() |
104 | |
105 | - def _handle_select(self): |
106 | + def _handle_select(self, event=None): |
107 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
108 | - track = self.track_menu.get_current_menuitem().get_userdata() |
109 | + track = self.track_menu.selected_userdata |
110 | kwargs = { 'track' : track } |
111 | self.callback("audio_play", kwargs) |
112 | |
113 | + def _on_menu_selected(self, actor=None): |
114 | + '''Handle a *select command* if an item was selected.''' |
115 | + self._handle_select() |
116 | + |
117 | + def _display_selected_track(self, actor=None): |
118 | + '''Update of the list indicator and the screen's title''' |
119 | + self.li.set_current(self.track_menu.selected_index + 1) |
120 | + track = self.track_menu.selected_userdata |
121 | + self.screen_title.set_text(track.get_artist()) |
122 | + self.screen_title.show() |
123 | + |
124 | |
125 | === modified file 'entertainerlib/frontend/gui/screens/disc.py' |
126 | --- entertainerlib/frontend/gui/screens/disc.py 2009-05-10 07:43:27 +0000 |
127 | +++ entertainerlib/frontend/gui/screens/disc.py 2009-07-19 20:23:56 +0000 |
128 | @@ -18,7 +18,6 @@ |
129 | LoadingAnimation) |
130 | from entertainerlib.frontend.gui.widgets.texture import Texture |
131 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
132 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
133 | from entertainerlib.frontend.medialibrary.playlist import Playlist |
134 | from entertainerlib.utils.albumart_downloader import AlbumArtDownloader |
135 | |
136 | @@ -69,9 +68,14 @@ |
137 | |
138 | self.playlist = Playlist(tracks) |
139 | self._create_album_info(title, artist, tracks, disc.get_length()) |
140 | - self._create_track_menu(tracks) |
141 | + self.track_menu = self._create_track_menu(tracks) |
142 | + self.add(self.track_menu) |
143 | self._create_album_cover_texture(artist, title) |
144 | - self._create_list_indicator() |
145 | + |
146 | + self.li = ListIndicator(0.75, 0.8, 0.2, 0.045, |
147 | + ListIndicator.VERTICAL) |
148 | + self.li.set_maximum(len(tracks)) |
149 | + self.add(self.li) |
150 | |
151 | art_file = os.path.join(self.config.ALBUM_ART_DIR, |
152 | artist + " - " + title + ".jpg") |
153 | @@ -172,45 +176,30 @@ |
154 | Create a track menu. This menu contains list of all tracks on album. |
155 | @param tracks: List of CompactDisc objects |
156 | """ |
157 | - self.track_menu = TextMenu(self.theme, self.config.show_effects()) |
158 | - self.track_menu.set_row_count(1) |
159 | - self.track_menu.set_visible_column_count(7) |
160 | - self.track_menu.set_item_size(self.get_abs_x(0.4393), |
161 | - self.get_abs_y(0.0781)) |
162 | - self.track_menu.set_position(self.get_abs_x(0.4978), |
163 | - self.get_abs_y(0.2344)) |
164 | - |
165 | - for index, track in enumerate(tracks): |
166 | - length = str(track.get_length() / 60) + ":" + \ |
167 | - str(track.get_length() % 60).zfill(2) |
168 | - item = TextMenuItem(0.4393, 0.0781, track.get_title(), length) |
169 | - item.set_userdata(index) |
170 | - self.track_menu.add_actor(item) |
171 | - self.track_menu.set_active(True) |
172 | - self.add(self.track_menu) |
173 | - |
174 | - def _create_list_indicator(self): |
175 | - '''Create list indicator for track list.''' |
176 | - self.li = ListIndicator(0.75, 0.8, 0.2, 0.045, ListIndicator.VERTICAL) |
177 | - self.li.set_maximum(self.track_menu.get_number_of_items()) |
178 | - self.add(self.li) |
179 | - |
180 | - def _move_menu(self, menu_direction): |
181 | - '''Move the text menu in the provided direction.''' |
182 | - self.track_menu.move(menu_direction) |
183 | - self.li.set_current(self.track_menu.get_current_position() + 1) |
184 | + menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781) |
185 | + menu.visible_rows = 7 |
186 | + |
187 | + tracks_list = [[track.get_title(), track.get_length_string(), index] \ |
188 | + for index, track in enumerate(tracks)] |
189 | + menu.async_add(tracks_list) |
190 | + |
191 | + menu.active = True |
192 | + menu.connect('selected', self._handle_select) |
193 | + menu.connect('moved', self._display_selected_track) |
194 | + |
195 | + return menu |
196 | |
197 | def _handle_up(self): |
198 | '''Handle UserEvent.NAVIGATE_UP.''' |
199 | - self._move_menu(TextMenu.UP) |
200 | + self.track_menu.up() |
201 | |
202 | def _handle_down(self): |
203 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
204 | - self._move_menu(TextMenu.DOWN) |
205 | + self.track_menu.down() |
206 | |
207 | - def _handle_select(self): |
208 | + def _handle_select(self, event=None): |
209 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
210 | - track_index = self.track_menu.get_current_menuitem().get_userdata() |
211 | + track_index = self.track_menu.selected_userdata |
212 | self.playlist.set_current(track_index) |
213 | self.media_player.set_playlist(self.playlist) |
214 | self.media_player.play() |
215 | @@ -220,3 +209,7 @@ |
216 | if self.has_disc: |
217 | Screen.handle_user_event(self, event) |
218 | |
219 | + def _display_selected_track(self, event=None): |
220 | + '''Update of the list indicator.''' |
221 | + self.li.set_current(self.track_menu.selected_index + 1) |
222 | + |
223 | |
224 | === modified file 'entertainerlib/frontend/gui/screens/feed.py' |
225 | --- entertainerlib/frontend/gui/screens/feed.py 2009-06-25 19:06:28 +0000 |
226 | +++ entertainerlib/frontend/gui/screens/feed.py 2009-07-12 07:57:05 +0000 |
227 | @@ -8,7 +8,6 @@ |
228 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
229 | from entertainerlib.frontend.gui.widgets.texture import Texture |
230 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
231 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
232 | from entertainerlib.utils.feed_utils import FeedEntryParser |
233 | |
234 | class Feed(Screen): |
235 | @@ -21,7 +20,8 @@ |
236 | self.feed = feed |
237 | self.menu = None |
238 | |
239 | - self._create_entry_menu() |
240 | + self.menu = self._create_entry_menu() |
241 | + self.add(self.menu) |
242 | |
243 | # Screen Title (Displayed at the bottom left corner) |
244 | screen_title = Label(0.13, "screentitle", 0, 0.87, _("Feed")) |
245 | @@ -56,44 +56,47 @@ |
246 | |
247 | #List indicator |
248 | self.li = ListIndicator(0.57, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) |
249 | - self.li.set_maximum(self.menu.get_number_of_items()) |
250 | + self.li.set_maximum(len(self.feed.entries)) |
251 | self.add(self.li) |
252 | |
253 | + self._display_selected_feed() |
254 | + |
255 | + self.menu.connect('selected', self._on_menu_selected) |
256 | + self.menu.connect('moved', self._display_selected_feed) |
257 | + |
258 | def _create_entry_menu(self): |
259 | - """ |
260 | - Create Entries-menu. This menu contains list of entries. |
261 | - """ |
262 | - self.menu = TextMenu(self.theme, self.config.show_effects()) |
263 | - self.menu.set_row_count(1) |
264 | - self.menu.set_item_size(self.get_abs_x(0.5124), self.get_abs_y(0.0781)) |
265 | - self.menu.set_position(self.get_abs_x(0.2438), self.get_abs_y(0.2604)) |
266 | - |
267 | - # Create menu items based on FeedLibrary |
268 | - for entry in self.feed.entries: |
269 | - item = TextMenuItem(0.5124, 0.0781, |
270 | - FeedEntryParser().strip_tags(entry.title)) |
271 | - item.set_userdata(entry) |
272 | - self.menu.add_actor(item) |
273 | - |
274 | - self.menu.set_active(True) |
275 | - self.add(self.menu) |
276 | - |
277 | - def _move_menu(self, menu_direction): |
278 | - '''Move the text menu in the given direction.''' |
279 | - self.menu.move(menu_direction) |
280 | - self.li.set_current(self.menu.get_current_position() + 1) |
281 | + """Create Entries-menu. This menu contains list of entries.""" |
282 | + menu = TextMenu(0.2438, 0.2604, 0.5124, 0.0781) |
283 | + |
284 | + parser = FeedEntryParser() |
285 | + feeds = self.feed.entries |
286 | + feeds_list = [[parser.strip_tags(feed.title), None, feed] \ |
287 | + for feed in feeds] |
288 | + menu.async_add(feeds_list) |
289 | + |
290 | + menu.active = True |
291 | + |
292 | + return menu |
293 | |
294 | def _handle_up(self): |
295 | '''Handle UserEvent.NAVIGATE_UP.''' |
296 | - self._move_menu(TextMenu.UP) |
297 | + self.menu.up() |
298 | |
299 | def _handle_down(self): |
300 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
301 | - self._move_menu(TextMenu.DOWN) |
302 | + self.menu.down() |
303 | |
304 | - def _handle_select(self): |
305 | + def _handle_select(self, event=None): |
306 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
307 | - entry = self.menu.get_current_menuitem().get_userdata() |
308 | + entry = self.menu.selected_userdata |
309 | kwargs = { 'feed' : self.feed, 'entry' : entry } |
310 | self.callback("entry", kwargs) |
311 | |
312 | + def _on_menu_selected(self, event=None): |
313 | + '''Handle a *select command* if an item was selected.''' |
314 | + self._handle_select() |
315 | + |
316 | + def _display_selected_feed(self, event=None): |
317 | + '''Update of the list indicator''' |
318 | + self.li.set_current(self.menu.selected_index + 1) |
319 | + |
320 | |
321 | === modified file 'entertainerlib/frontend/gui/screens/feed_entry.py' |
322 | --- entertainerlib/frontend/gui/screens/feed_entry.py 2009-06-21 22:38:01 +0000 |
323 | +++ entertainerlib/frontend/gui/screens/feed_entry.py 2009-06-25 18:56:15 +0000 |
324 | @@ -71,8 +71,8 @@ |
325 | entry_text.set_use_markup(True) |
326 | entry_text.set_size(0.5857, 0.5208) |
327 | |
328 | - self.scroll_area = ScrollArea(0.5857, 0.4948, 0.2072, 0.3, |
329 | - entry_text, active=True) |
330 | + self.scroll_area = ScrollArea(0.2072, 0.3, 0.5857, 0.4948, |
331 | + entry_text) |
332 | self.add(self.scroll_area) |
333 | |
334 | # Add the additional actions that are needed but not handled by default |
335 | |
336 | === modified file 'entertainerlib/frontend/gui/screens/main.py' |
337 | --- entertainerlib/frontend/gui/screens/main.py 2009-06-28 20:12:13 +0000 |
338 | +++ entertainerlib/frontend/gui/screens/main.py 2009-07-19 19:27:31 +0000 |
339 | @@ -9,7 +9,6 @@ |
340 | from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu |
341 | from entertainerlib.frontend.gui.widgets.texture import Texture |
342 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
343 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
344 | from entertainerlib.utils.cd_utils import eject_cd |
345 | from entertainerlib.utils.feed_utils import FeedEntryParser |
346 | |
347 | @@ -47,23 +46,27 @@ |
348 | |
349 | self.menu = self._create_main_menu() |
350 | self.add(self.menu) |
351 | - self.menu.connect('selected', self._on_menu_selected) |
352 | + |
353 | + self._update_preview_area() |
354 | + |
355 | + self.add(ClockLabel(0.13, "screentitle", 0, 0.87)) |
356 | + |
357 | + self.menu.connect('selected', self._handle_select) |
358 | self.menu.connect('moved', self._on_menu_moved) |
359 | - self.menu.connect('activated', self._main_menu_activation) |
360 | + self.menu.connect('activated', self._on_menu_activated) |
361 | + |
362 | self.menu.active = True |
363 | |
364 | - self.add(ClockLabel(0.13, "screentitle", 0, 0.87)) |
365 | - |
366 | def get_type(self): |
367 | """Return screen type.""" |
368 | return Screen.NORMAL |
369 | |
370 | def get_name(self): |
371 | - """Return screen name (human readble)""" |
372 | + """Return screen name (human readble).""" |
373 | return "Main" |
374 | |
375 | def _create_main_menu(self): |
376 | - """Create main menu of the home screen""" |
377 | + """Create main menu of the home screen.""" |
378 | menu = ScrollMenu(10, 60, 0.045, "menuitem_active") |
379 | menu.set_name("mainmenu") |
380 | |
381 | @@ -96,24 +99,18 @@ |
382 | '''Create the RSS preview menu that will show feed highlights. An |
383 | uninitialized menu will be returned to prevent move errors if there |
384 | is nothing in the feed library.''' |
385 | - menu = TextMenu(self.theme, self.config.show_effects()) |
386 | + menu = TextMenu(0.035, 0.12, 0.549, 0.078) |
387 | |
388 | if self.feed_library.is_empty() is False: |
389 | - menu.set_row_count(1) |
390 | - menu.set_position(self.get_abs_x(0.035), self.get_abs_y(0.12)) |
391 | - menu.set_item_size(self.get_abs_x(0.549), self.get_abs_y(0.078)) |
392 | - |
393 | - # List of latest entries. pack = (Feed object, Entry object) |
394 | + parser = FeedEntryParser() |
395 | entries = self.feed_library.get_latest_entries(5) |
396 | - for pack in entries: |
397 | - text = pack[0].title + " - " + pack[1].title |
398 | - item = TextMenuItem(0.549, 0.078, |
399 | - FeedEntryParser().strip_tags(text), pack[1].date) |
400 | - kwargs = { 'feed' : pack[0], 'entry' : pack[1] } |
401 | - item.set_userdata(kwargs) |
402 | - menu.add_actor(item) |
403 | + entries_list = [[parser.strip_tags(entry[0].title + " - " + \ |
404 | + entry[1].title), entry[1].date, \ |
405 | + { 'feed' : entry[0], 'entry' : entry[1] }] for entry in entries] |
406 | + menu.async_add(entries_list) |
407 | |
408 | - menu.set_active(False) |
409 | + menu.connect('selected', self._handle_select) |
410 | + menu.connect('activated', self._on_rss_menu_activated) |
411 | |
412 | return menu |
413 | |
414 | @@ -198,11 +195,15 @@ |
415 | # No headlines available in the library |
416 | info = Label(0.05, "title", 0.1, 0.35, _("No headlines available")) |
417 | preview.add(info) |
418 | + self.rss_preview_menu = None |
419 | |
420 | return preview |
421 | |
422 | def _update_preview_area(self): |
423 | '''Update the preview area to display the current menu item.''' |
424 | + if self.rss_preview_menu: |
425 | + rss_preview_menu_active = self.rss_preview_menu.active |
426 | + |
427 | self.preview.remove_all() |
428 | item = self.menu.get_selected() |
429 | |
430 | @@ -214,10 +215,8 @@ |
431 | self.preview.add(self._create_playing_preview()) |
432 | elif item.get_name() == "rss": |
433 | self.preview.add(self._create_rss_preview()) |
434 | - # When we update the rss preview menu, we must set it active if |
435 | - # main menu is not. |
436 | - if not self.menu.active: |
437 | - self.rss_preview_menu.set_active(True) |
438 | + if self.rss_preview_menu: |
439 | + self.rss_preview_menu.active = rss_preview_menu_active |
440 | else: |
441 | update = False |
442 | |
443 | @@ -225,7 +224,7 @@ |
444 | if update: |
445 | fade_in = clutter.Timeline(20, 60) |
446 | alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func) |
447 | - self.behaviour = clutter.BehaviourOpacity(0x00, 0xff, alpha_in) |
448 | + self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in) |
449 | self.behaviour.apply(self.preview) |
450 | fade_in.start() |
451 | |
452 | @@ -257,25 +256,25 @@ |
453 | if self.menu.active: |
454 | self.menu.scroll_up() |
455 | else: |
456 | - self.rss_preview_menu.move(TextMenu.UP) |
457 | + self.rss_preview_menu.up() |
458 | |
459 | def _handle_down(self): |
460 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
461 | if self.menu.active: |
462 | self.menu.scroll_down() |
463 | else: |
464 | - self.rss_preview_menu.move(TextMenu.DOWN) |
465 | + self.rss_preview_menu.down() |
466 | |
467 | def _handle_left(self): |
468 | '''Handle UserEvent.NAVIGATE_LEFT.''' |
469 | if self._can_move_horizontally(): |
470 | - self._preview_activation() |
471 | + self.rss_preview_menu.active = True |
472 | |
473 | def _handle_right(self): |
474 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
475 | self.menu.active = True |
476 | |
477 | - def _handle_select(self): |
478 | + def _handle_select(self, event=None): |
479 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
480 | item = self.menu.get_selected() |
481 | |
482 | @@ -297,27 +296,20 @@ |
483 | if self.menu.active: |
484 | self.callback("rss") |
485 | else: |
486 | - menu_item = self.rss_preview_menu.get_current_menuitem() |
487 | - kwargs = menu_item.get_userdata() |
488 | + menu_item = self.rss_preview_menu.selected_item |
489 | + kwargs = menu_item.userdata |
490 | self.callback("entry", kwargs) |
491 | |
492 | - def _on_menu_moved(self, actor): |
493 | + def _on_menu_moved(self, event): |
494 | '''Update preview area when selected item changed on the menu.''' |
495 | self._update_preview_area() |
496 | |
497 | - def _on_menu_selected(self, actor): |
498 | - '''Handle a *select command* if an item was selected.''' |
499 | - self._handle_select() |
500 | - |
501 | - def _main_menu_activation(self, actor=None): |
502 | + def _on_menu_activated(self, event=None): |
503 | '''Handle the main menu activation.''' |
504 | - self.menu.active = True |
505 | - if self.rss_preview_menu: |
506 | - self.rss_preview_menu.set_active(False) |
507 | + if self.rss_preview_menu is not None: |
508 | + self.rss_preview_menu.active = False |
509 | |
510 | - def _preview_activation(self, actor=None): |
511 | - '''Handle the preview activation.''' |
512 | + def _on_rss_menu_activated(self, event=None): |
513 | + '''Handle the rss menu activation.''' |
514 | self.menu.active = False |
515 | - if self.rss_preview_menu: |
516 | - self.rss_preview_menu.set_active(True) |
517 | |
518 | |
519 | === modified file 'entertainerlib/frontend/gui/screens/movie.py' |
520 | --- entertainerlib/frontend/gui/screens/movie.py 2009-06-05 05:10:21 +0000 |
521 | +++ entertainerlib/frontend/gui/screens/movie.py 2009-07-21 22:22:53 +0000 |
522 | @@ -13,7 +13,6 @@ |
523 | from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
524 | from entertainerlib.frontend.gui.widgets.texture import Texture |
525 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
526 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
527 | |
528 | class Movie(Screen): |
529 | '''Screen contains information of one movie.''' |
530 | @@ -110,7 +109,7 @@ |
531 | plot.set_line_wrap_mode(pango.WRAP_WORD) |
532 | plot.set_line_wrap(True) |
533 | plot.width = 0.5124 |
534 | - self.scroll_area = ScrollArea(0.5124, 0.3516, 0.33, 0.38, plot) |
535 | + self.scroll_area = ScrollArea(0.33, 0.38, 0.5124, 0.3516, plot) |
536 | self.add(self.scroll_area) |
537 | |
538 | # Actors |
539 | @@ -156,47 +155,40 @@ |
540 | """ |
541 | Create menu of the screen. This displayed the left side of the screen. |
542 | """ |
543 | - self.menu = TextMenu(self.theme, self.config.show_effects()) |
544 | - self.menu.set_row_count(1) |
545 | - #self.track_menu.set_visible_column_count(10) |
546 | - self.menu.set_item_size(self.get_abs_x(0.2196), self.get_abs_y(0.0781)) |
547 | - self.menu.set_position(self.get_abs_x(0.07), self.get_abs_y(0.1)) |
548 | - |
549 | - item1 = TextMenuItem(0.2196, 0.0781, _("Watch")) |
550 | - item1.set_userdata("watch") |
551 | - self.menu.add_actor(item1) |
552 | - |
553 | - self.menu.set_active(True) |
554 | + self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781) |
555 | + self.menu.add_item(_("Watch"), None, "watch") |
556 | + self.menu.active = True |
557 | + |
558 | self.add(self.menu) |
559 | |
560 | def _handle_up(self): |
561 | '''Handle UserEvent.NAVIGATE_UP.''' |
562 | - if self.menu.is_active(): |
563 | - self.menu.move(self.menu.UP) |
564 | + if self.menu.active: |
565 | + self.menu.up() |
566 | else: |
567 | self.scroll_area.scroll_up() |
568 | |
569 | def _handle_down(self): |
570 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
571 | - if self.menu.is_active(): |
572 | - self.menu.move(self.menu.DOWN) |
573 | + if self.menu.active: |
574 | + self.menu.down() |
575 | else: |
576 | self.scroll_area.scroll_down() |
577 | |
578 | def _handle_left(self): |
579 | '''Handle UserEvent.NAVIGATE_LEFT.''' |
580 | - self.menu.set_active(True) |
581 | - self.scroll_area.set_active(False) |
582 | + self.menu.active = True |
583 | + self.scroll_area.active = False |
584 | |
585 | def _handle_right(self): |
586 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
587 | - self.menu.set_active(False) |
588 | - self.scroll_area.set_active(True) |
589 | + self.menu.active = False |
590 | + self.scroll_area.active = True |
591 | |
592 | - def _handle_select(self): |
593 | + def _handle_select(self, event=None): |
594 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
595 | - if self.menu.is_active(): |
596 | - item = self.menu.get_current_menuitem().get_userdata() |
597 | + if self.menu.active: |
598 | + item = self.menu.selected_userdata |
599 | if item == "watch": |
600 | self.media_player.set_media(self.movie) |
601 | self.media_player.play() |
602 | |
603 | === modified file 'entertainerlib/frontend/gui/screens/photo.py' |
604 | --- entertainerlib/frontend/gui/screens/photo.py 2009-05-10 06:24:43 +0000 |
605 | +++ entertainerlib/frontend/gui/screens/photo.py 2009-05-21 16:12:09 +0000 |
606 | @@ -182,7 +182,7 @@ |
607 | else: |
608 | self.texture.move_by(-self.MOVE_SIZE, 0) |
609 | |
610 | - def _handle_select(self): |
611 | + def _handle_select(self, event=None): |
612 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
613 | # Zoom image. If we zoom then we stop sliding photos. |
614 | self.stop_slideshow() |
615 | |
616 | === modified file 'entertainerlib/frontend/gui/screens/photo_albums.py' |
617 | --- entertainerlib/frontend/gui/screens/photo_albums.py 2009-05-10 07:43:27 +0000 |
618 | +++ entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-19 19:27:31 +0000 |
619 | @@ -13,7 +13,6 @@ |
620 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
621 | from entertainerlib.frontend.gui.widgets.texture import Texture |
622 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
623 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
624 | |
625 | class PhotoAlbums(Screen): |
626 | '''Screen contains a list of photo albums and album previews.''' |
627 | @@ -46,10 +45,16 @@ |
628 | self.in_opacity = None |
629 | self.out_opacity = None |
630 | self.preview_textures = None |
631 | - self._create_album_menu() |
632 | + self.menu = self._create_album_menu() |
633 | + self.add(self.menu) |
634 | self.li = None |
635 | self._create_list_indicator() |
636 | |
637 | + self._update_album_preview(self.menu.selected_userdata) |
638 | + |
639 | + self.menu.connect('selected', self._handle_select) |
640 | + self.menu.connect('moved', self._display_selected_album) |
641 | + |
642 | def _create_no_photos_information(self): |
643 | """ |
644 | Create textures and labels for information screen. This is displayed |
645 | @@ -80,28 +85,17 @@ |
646 | Create ImageAlbum-menu. This menu contains list of albums. It also |
647 | displays number of photographs per album. |
648 | """ |
649 | - self.menu = TextMenu(self.theme, self.config.show_effects()) |
650 | - self.menu.set_row_count(1) |
651 | - self.menu.set_visible_column_count(7) |
652 | - self.menu.set_item_size(self.get_abs_x(0.4393), self.get_abs_y(0.0781)) |
653 | - self.menu.set_position(self.get_abs_x(0.5271), self.get_abs_y(0.3385)) |
654 | + menu = TextMenu(0.5271, 0.3385, 0.4393, 0.0781) |
655 | + menu.visible_rows = 7 |
656 | |
657 | - # Create menu items based on ImageLibrary |
658 | albums = self.image_library.get_albums() |
659 | - for album in albums: |
660 | - nro_of_photos = album.get_number_of_images() |
661 | - if nro_of_photos == 0: # Skip empty albums |
662 | - continue |
663 | - else: |
664 | - nro_of_photos = str(nro_of_photos) |
665 | - item = TextMenuItem(0.4393, 0.0781, album.get_title(), |
666 | - nro_of_photos) |
667 | - item.set_userdata(album) |
668 | - self.menu.add_actor(item) |
669 | - |
670 | - self.add(self.menu) |
671 | - self._update_album_preview( |
672 | - self.menu.get_current_menuitem().get_userdata()) |
673 | + albums_list = [[album.get_title(), str(album.get_number_of_images()), |
674 | + album] for album in albums if album.get_number_of_images() != 0] |
675 | + menu.async_add(albums_list) |
676 | + |
677 | + menu.active = True |
678 | + |
679 | + return menu |
680 | |
681 | def _create_album_preview(self, album): |
682 | """ |
683 | @@ -246,30 +240,23 @@ |
684 | def _create_list_indicator(self): |
685 | '''Create list indicator for albums list.''' |
686 | self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) |
687 | - self.li.set_maximum(self.menu.get_number_of_items()) |
688 | + albums = self.image_library.get_albums() |
689 | + albums_list = [[album.get_title(), str(album.get_number_of_images()), |
690 | + album] for album in albums if album.get_number_of_images() != 0] |
691 | + self.li.set_maximum(len(albums_list)) |
692 | self.add(self.li) |
693 | |
694 | - def _move_menu(self, menu_direction): |
695 | - '''Move the menu in the given direction.''' |
696 | - self.menu.move(menu_direction) |
697 | - self.li.set_current(self.menu.get_current_position() + 1) |
698 | - # 500ms timeout before preview is updated (fast scrolling) |
699 | - if self.timeout is not None: |
700 | - gobject.source_remove(self.timeout) |
701 | - self.timeout = gobject.timeout_add(500, self._update_album_preview, |
702 | - self.menu.get_current_menuitem().get_userdata()) |
703 | - |
704 | def _handle_up(self): |
705 | '''Handle UserEvent.NAVIGATE_UP.''' |
706 | - self._move_menu(TextMenu.UP) |
707 | + self.menu.up() |
708 | |
709 | def _handle_down(self): |
710 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
711 | - self._move_menu(TextMenu.DOWN) |
712 | + self.menu.down() |
713 | |
714 | - def _handle_select(self): |
715 | + def _handle_select(self, event=None): |
716 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
717 | - album = self.menu.get_current_menuitem().get_userdata() |
718 | + album = self.menu.selected_userdata |
719 | kwargs = { 'title' : album.get_title(), 'images' : album.get_images() } |
720 | self.callback("photographs", kwargs) |
721 | |
722 | @@ -280,3 +267,12 @@ |
723 | else: |
724 | Screen.handle_user_event(self, event) |
725 | |
726 | + def _display_selected_album(self, event=None): |
727 | + '''Update of the list indicator''' |
728 | + self.li.set_current(self.menu.selected_index + 1) |
729 | + # 500ms timeout before preview is updated (fast scrolling) |
730 | + if self.timeout is not None: |
731 | + gobject.source_remove(self.timeout) |
732 | + self.timeout = gobject.timeout_add(500, self._update_album_preview, |
733 | + self.menu.selected_userdata) |
734 | + |
735 | |
736 | === modified file 'entertainerlib/frontend/gui/screens/photographs.py' |
737 | --- entertainerlib/frontend/gui/screens/photographs.py 2009-05-10 07:43:27 +0000 |
738 | +++ entertainerlib/frontend/gui/screens/photographs.py 2009-07-14 11:06:21 +0000 |
739 | @@ -2,13 +2,11 @@ |
740 | '''Photographs - Screen contains a list/grid of photographs''' |
741 | |
742 | import pango |
743 | -import gobject |
744 | |
745 | from entertainerlib.frontend.gui.screens.screen import Screen |
746 | from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
747 | from entertainerlib.frontend.gui.widgets.label import Label |
748 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
749 | -from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem |
750 | from entertainerlib.frontend.gui.widgets.loading_animation import ( |
751 | LoadingAnimation) |
752 | from entertainerlib.frontend.gui.widgets.texture import Texture |
753 | @@ -19,7 +17,6 @@ |
754 | def __init__(self, move_to_new_screen_callback, title, images): |
755 | Screen.__init__(self, 'Photographs', move_to_new_screen_callback) |
756 | |
757 | - self.theme = self.config.theme |
758 | self.images = images |
759 | |
760 | # Screen Title (Displayed at the bottom left corner) |
761 | @@ -46,54 +43,32 @@ |
762 | self._create_list_indicator() |
763 | |
764 | # Create photomenu |
765 | - self.menu = ImageMenu(self.theme) |
766 | - self.menu.set_visible_column_count(6) |
767 | - self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.1823)) |
768 | - |
769 | - if self.config.show_effects(): |
770 | - self.menu.set_animate(True) |
771 | - |
772 | + self.menu = ImageMenu(0.03, 0.08, 0.12, self.y_for_x(0.12)) |
773 | + self.menu.items_per_col = 3 |
774 | + self.menu.visible_rows = 3 |
775 | + self.menu.visible_cols = 8 |
776 | + self.menu.active = True |
777 | self.add(self.menu) |
778 | |
779 | photos = self.images |
780 | - self.behaviour = None |
781 | - gobject.timeout_add(25, self._create_photo_menuitems, photos) |
782 | - |
783 | - self._update_image_info(0) |
784 | - |
785 | - def _create_photo_menuitems(self, photographs): |
786 | - """ |
787 | - This function is called as async callback. It is also recursive |
788 | - function. This allows us load menu items in the background without |
789 | - blocking the user interface. |
790 | - """ |
791 | - # Check if we should stop recursive calls (menu is complete) |
792 | - if len(photographs) > 0: |
793 | - photo = photographs[0] |
794 | - texture = Texture(photo.get_thumbnail_url()) |
795 | - item = ImageMenuItem(0.1464, 0.75, texture) |
796 | - item.set_userdata(photo) |
797 | - self.menu.add_actor(item) |
798 | - # Recursive call, remove first artist from the list |
799 | - gobject.timeout_add(15, self._create_photo_menuitems, |
800 | - photographs[1:]) |
801 | - return False |
802 | - else: |
803 | - self.throbber.hide() |
804 | - return False |
805 | - |
806 | - def _update_image_info(self, number): |
807 | - """ |
808 | - Update image information box. |
809 | - @param number: Number of image. This is index of the self.__images |
810 | - """ |
811 | - image = self.images[number] |
812 | + photos_list = [[Texture(photo.get_thumbnail_url()), photo] \ |
813 | + for photo in photos] |
814 | + self.menu.async_add(photos_list) |
815 | + |
816 | + self.menu.connect("selected", self._handle_select) |
817 | + self.menu.connect('moved', self._update_image_info) |
818 | + self.menu.connect("filled", self._on_menu_filled) |
819 | + |
820 | + def _update_image_info(self, event=None): |
821 | + """Update image information box.""" |
822 | + image = self.images[self.menu.selected_index] |
823 | name = image.get_title() |
824 | desc = image.get_description() |
825 | self.image_title.set_text(name) |
826 | self.image_title.set_size(0.366, 0.04167) |
827 | self.image_desc.set_text(desc) |
828 | self.image_desc.set_size(0.366, 0.0911) |
829 | + self.li.set_current(self.menu.selected_index + 1) |
830 | |
831 | def _create_list_indicator(self): |
832 | '''Create list indicator for albums list.''' |
833 | @@ -102,31 +77,29 @@ |
834 | self.li.set_maximum(len(self.images)) |
835 | self.add(self.li) |
836 | |
837 | - def _move_menu(self, menu_direction): |
838 | - '''Move the menu in the given direction.''' |
839 | - self.menu.move(menu_direction) |
840 | - self._update_image_info(self.menu.get_current_position()) |
841 | - self.li.set_current(self.menu.get_current_position() + 1) |
842 | - |
843 | def _handle_up(self): |
844 | '''Handle UserEvent.NAVIGATE_UP.''' |
845 | - self._move_menu(ImageMenu.UP) |
846 | + self.menu.up() |
847 | |
848 | def _handle_down(self): |
849 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
850 | - self._move_menu(ImageMenu.DOWN) |
851 | + self.menu.down() |
852 | |
853 | def _handle_left(self): |
854 | '''Handle UserEvent.NAVIGATE_LEFT.''' |
855 | - self._move_menu(ImageMenu.LEFT) |
856 | + self.menu.left() |
857 | |
858 | def _handle_right(self): |
859 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
860 | - self._move_menu(ImageMenu.RIGHT) |
861 | + self.menu.right() |
862 | |
863 | - def _handle_select(self): |
864 | + def _handle_select(self, event=None): |
865 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
866 | - index = self.menu.get_current_position() |
867 | + index = self.menu.selected_index |
868 | kwargs = {'current_photo_index' : index, 'images' : self.images} |
869 | self.callback("photo", kwargs) |
870 | |
871 | + def _on_menu_filled(self, event=None): |
872 | + '''Handles filled event.''' |
873 | + self.throbber.hide() |
874 | + |
875 | |
876 | === modified file 'entertainerlib/frontend/gui/screens/question.py' |
877 | --- entertainerlib/frontend/gui/screens/question.py 2009-06-02 01:16:03 +0000 |
878 | +++ entertainerlib/frontend/gui/screens/question.py 2009-06-27 09:03:33 +0000 |
879 | @@ -4,7 +4,6 @@ |
880 | from entertainerlib.frontend.gui.screens.screen import Screen |
881 | from entertainerlib.frontend.gui.widgets.label import Label |
882 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
883 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
884 | |
885 | class Question(Screen): |
886 | '''Screen is displayed when the application needs to ask a close ended |
887 | @@ -32,18 +31,14 @@ |
888 | |
889 | def display_answers(self): |
890 | '''Display a menu with answers on the screen.''' |
891 | - self.menu = TextMenu(self.config.theme, self.config.show_effects()) |
892 | + self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781) |
893 | self.menu.set_name("questionmenu") |
894 | - self.menu.set_row_count(1) |
895 | - self.menu.set_visible_column_count(7) |
896 | - self.menu.set_item_size(self.get_abs_x(0.4393), self.get_abs_y(0.0781)) |
897 | - self.menu.set_position(self.get_abs_x(0.095), self.get_abs_y(0.5)) |
898 | + self.menu.connect('selected', self._handle_select) |
899 | |
900 | for answer in self.answers: |
901 | - item = TextMenuItem(0.4173, 0.0781, str(answer)) |
902 | - item.set_userdata(str(answer)) |
903 | - self.menu.add_actor(item) |
904 | + self.menu.add_item(str(answer), None, str(answer)) |
905 | |
906 | + self.menu.active = True |
907 | self.add(self.menu) |
908 | |
909 | def question_callback(self, selected_answer): |
910 | @@ -63,20 +58,16 @@ |
911 | '''Go back to previous screen''' |
912 | self.move_to_previous_screen_callback() |
913 | |
914 | - def _move_menu(self, menu_direction): |
915 | - '''Move the menu in the given direction.''' |
916 | - self.menu.move(menu_direction) |
917 | - |
918 | def _handle_up(self): |
919 | '''Handle UserEvent.NAVIGATE_UP.''' |
920 | - self._move_menu(TextMenu.UP) |
921 | + self.menu.up() |
922 | |
923 | def _handle_down(self): |
924 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
925 | - self._move_menu(TextMenu.DOWN) |
926 | + self.menu.down() |
927 | |
928 | - def _handle_select(self): |
929 | + def _handle_select(self, event=None): |
930 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
931 | - self.question_callback(self.menu.get_current_menuitem().get_userdata()) |
932 | + self.question_callback(self.menu.selected_userdata) |
933 | self.go_back() |
934 | |
935 | |
936 | === modified file 'entertainerlib/frontend/gui/screens/rss.py' |
937 | --- entertainerlib/frontend/gui/screens/rss.py 2009-06-25 19:06:28 +0000 |
938 | +++ entertainerlib/frontend/gui/screens/rss.py 2009-07-19 19:27:31 +0000 |
939 | @@ -5,13 +5,9 @@ |
940 | |
941 | from entertainerlib.frontend.gui.screens.screen import Screen |
942 | from entertainerlib.frontend.gui.widgets.label import Label |
943 | -from entertainerlib.frontend.gui.widgets.menu import Menu |
944 | -from entertainerlib.frontend.gui.widgets.selector import Selector |
945 | from entertainerlib.frontend.gui.widgets.texture import Texture |
946 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
947 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
948 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
949 | -from entertainerlib.frontend.medialibrary.feeds import Feed |
950 | from entertainerlib.utils.feed_utils import FeedEntryParser |
951 | |
952 | class Rss(Screen): |
953 | @@ -21,9 +17,9 @@ |
954 | Screen.__init__(self, 'Rss', move_to_new_screen_callback) |
955 | |
956 | self.feed_order = 'UNREAD' # Feeds ordered by number of unread entries |
957 | - self.active_menu = "feed" |
958 | self.theme = self.config.theme |
959 | self.library = feed_library |
960 | + self.parser = FeedEntryParser() |
961 | |
962 | # Screen Title (Displayed at the bottom left corner) |
963 | screen_title = Label(0.13, "screentitle", 0, 0.87, _("Headlines")) |
964 | @@ -47,11 +43,12 @@ |
965 | else: |
966 | # Menus |
967 | self._create_context_menu() |
968 | - self._create_feed_menu() |
969 | + self.menu = self._create_feed_menu() |
970 | + self.add(self.menu) |
971 | self.li = None |
972 | self._create_feed_menu_indicator() |
973 | - self.menu.set_active(True) |
974 | - self.context_menu.set_active(False) |
975 | + self.menu.active = True |
976 | + self.context_menu.active = False |
977 | |
978 | # Feeds Title |
979 | feeds_title = Label(0.04167, "title", 0.3953, 0.2, _("Feeds")) |
980 | @@ -62,45 +59,41 @@ |
981 | self.add(unread_title) |
982 | |
983 | # Feed description text |
984 | - desc_text = FeedEntryParser().strip_tags( |
985 | - Feed(self.menu.get_current_menuitem().get_userdata() |
986 | - ).description) |
987 | + desc_text = self.parser.strip_tags( |
988 | + self.menu.selected_userdata.description) |
989 | + |
990 | self.description = Label(0.03646, "text", 0.3953, 0.9, desc_text) |
991 | self.description.set_ellipsize(pango.ELLIPSIZE_END) |
992 | self.description.set_use_markup(True) |
993 | self.description.set_size(0.5088, 0.0651) |
994 | self.add(self.description) |
995 | |
996 | + self.menu.connect('selected', self._handle_select) |
997 | + self.menu.connect('moved', self._display_selected_feed) |
998 | + self.menu.connect('activated', self._menu_activation) |
999 | + self.context_menu.connect('selected', self._handle_select) |
1000 | + self.context_menu.connect('activated', |
1001 | + self._context_menu_activation) |
1002 | + |
1003 | def _create_feed_menu(self): |
1004 | """ |
1005 | Create Feed-menu. This menu contains list of Feeds. It also displays |
1006 | number of unread entries per feed. |
1007 | """ |
1008 | - self.menu = TextMenu(self.theme, self.config.show_effects()) |
1009 | - self.menu.set_row_count(1) |
1010 | - self.menu.set_visible_column_count(7) |
1011 | - self.menu.set_item_size(self.get_abs_x(0.5124), self.get_abs_y(0.0781)) |
1012 | - self.menu.set_position(self.get_abs_x(0.3807), self.get_abs_y(0.2604)) |
1013 | + menu = TextMenu(0.3807, 0.2604, 0.5124, 0.0781) |
1014 | |
1015 | - # Create menu items based on FeedLibrary |
1016 | feeds = self.library.get_feeds(self.feed_order) |
1017 | - for feed in feeds: |
1018 | - unread = feed.get_number_of_unread() |
1019 | - if unread == 0: |
1020 | - unread = None |
1021 | - else: |
1022 | - unread = str(unread) |
1023 | - item = TextMenuItem(0.5124, 0.0781, |
1024 | - FeedEntryParser().strip_tags(feed.title), unread) |
1025 | - item.set_userdata(feed.url) |
1026 | - self.menu.add_actor(item) |
1027 | + feeds_list = [[self.parser.strip_tags(feed.title), \ |
1028 | + str(feed.get_number_of_unread()), feed] \ |
1029 | + for feed in feeds] |
1030 | + menu.async_add(feeds_list) |
1031 | |
1032 | - self.add(self.menu) |
1033 | + return menu |
1034 | |
1035 | def _create_feed_menu_indicator(self): |
1036 | '''Create a ListIndicator for feed menu.''' |
1037 | self.li = ListIndicator(0.7, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) |
1038 | - self.li.set_maximum(self.menu.get_number_of_items()) |
1039 | + self.li.set_maximum(len(self.library.get_feeds(self.feed_order))) |
1040 | self.add(self.li) |
1041 | |
1042 | def _create_context_menu(self): |
1043 | @@ -108,24 +101,14 @@ |
1044 | Create RSS-feed context menu and add it to the screen. |
1045 | This menu contains buttons for update and sorting. |
1046 | """ |
1047 | - select = Selector(Selector.LARGE, self.theme) |
1048 | - self.context_menu = Menu() |
1049 | - |
1050 | - self.context_menu.set_loop(True) |
1051 | - item1 = TextMenuItem(0.2196, 0.0781, _("Update feeds")) |
1052 | - self.context_menu.add(item1) |
1053 | - item2 = TextMenuItem(0.2196, 0.0781, _("Mark all as read")) |
1054 | - self.context_menu.add(item2) |
1055 | - item3 = TextMenuItem(0.2196, 0.0781, _("Sort by name")) |
1056 | - self.context_menu.add(item3) |
1057 | - item4 = TextMenuItem(0.2196, 0.0781, _("Sort by unread")) |
1058 | - self.context_menu.add(item4) |
1059 | - |
1060 | - self.context_menu.set_selector(select) |
1061 | - self.context_menu.set_position(self.get_abs_x(0.0732), |
1062 | - self.get_abs_y(0.2604)) |
1063 | - self.context_menu.set_active(False) |
1064 | - self.context_menu.show_all() |
1065 | + self.context_menu = TextMenu(0.0732, 0.2604, 0.2196, 0.0781) |
1066 | + |
1067 | + self.context_menu.add_item(_("Update feeds")) |
1068 | + self.context_menu.add_item(_("Mark all as read")) |
1069 | + self.context_menu.add_item(_("Sort by name")) |
1070 | + self.context_menu.add_item(_("Sort by unread")) |
1071 | + |
1072 | + self.context_menu.active = False |
1073 | self.add(self.context_menu) |
1074 | |
1075 | def _create_no_feeds_information(self): |
1076 | @@ -159,64 +142,41 @@ |
1077 | Update screen widgets. This is called always when screen is poped from |
1078 | the screen history. |
1079 | """ |
1080 | - #remove current feed menu, create a new one and set it to active |
1081 | - selected_name = self.menu.get_current_menuitem().label.get_text() |
1082 | - |
1083 | - self.remove(self.menu) |
1084 | - self._create_feed_menu() |
1085 | - self.menu.set_active(True) |
1086 | - |
1087 | - index = self.menu.get_index(selected_name) |
1088 | - if not index == -1: |
1089 | - self.menu.select(index) |
1090 | - |
1091 | - def _move_menu(self, menu_direction): |
1092 | - '''Move the menu in the given direction.''' |
1093 | - if menu_direction == TextMenu.UP: |
1094 | - scroll_direction = self.context_menu.scroll_up |
1095 | - elif menu_direction == TextMenu.DOWN: |
1096 | - scroll_direction = self.context_menu.scroll_down |
1097 | - |
1098 | - if self.active_menu == "feed": |
1099 | - self.menu.move(menu_direction) |
1100 | - self.li.set_current(self.menu.get_current_position() + 1) |
1101 | - feed = Feed(self.menu.get_current_menuitem().get_userdata()) |
1102 | - self.description.set_text(FeedEntryParser().strip_tags( |
1103 | - feed.description)) |
1104 | - self.description.set_size(0.5088, 0.0651) |
1105 | - else: |
1106 | - scroll_direction() |
1107 | + feeds = self.library.get_feeds(self.feed_order) |
1108 | + for idx, item in enumerate(self.menu.items): |
1109 | + feed = feeds[idx] |
1110 | + item.userdata = feed |
1111 | + item.update(self.parser.strip_tags(feed.title), |
1112 | + str(feed.get_number_of_unread())) |
1113 | |
1114 | def _handle_up(self): |
1115 | '''Handle UserEvent.NAVIGATE_UP.''' |
1116 | - self._move_menu(TextMenu.UP) |
1117 | + if self.menu.active: |
1118 | + self.menu.up() |
1119 | + else: |
1120 | + self.context_menu.up() |
1121 | |
1122 | def _handle_down(self): |
1123 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
1124 | - self._move_menu(TextMenu.DOWN) |
1125 | + if self.menu.active: |
1126 | + self.menu.down() |
1127 | + else: |
1128 | + self.context_menu.down() |
1129 | |
1130 | def _handle_left(self): |
1131 | '''Handle UserEvent.NAVIGATE_LEFT.''' |
1132 | - if self.active_menu == "feed": |
1133 | - self.active_menu = "context" |
1134 | - self.li.hide_position() |
1135 | - self.menu.set_active(False) |
1136 | - self.description.hide() |
1137 | - self.context_menu.set_active(True) |
1138 | + if self.menu.active: |
1139 | + self._context_menu_activation() |
1140 | |
1141 | def _handle_right(self): |
1142 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
1143 | - if self.active_menu == "context": |
1144 | - self.active_menu = "feed" |
1145 | - self.li.show_position() |
1146 | - self.menu.set_active(True) |
1147 | - self.description.show() |
1148 | - self.context_menu.set_active(False) |
1149 | + if not self.menu.active: |
1150 | + self._menu_activation() |
1151 | |
1152 | - def _handle_select(self): |
1153 | + def _handle_select(self, event=None): |
1154 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
1155 | - if self.active_menu == "context": |
1156 | - index = self.context_menu.get_current_position() |
1157 | + if not self.menu.active: |
1158 | + index = self.context_menu.selected_index |
1159 | if index == 0: |
1160 | # Send message to bus and update screen |
1161 | self.library.request_feed_update() |
1162 | @@ -235,14 +195,34 @@ |
1163 | self.update() |
1164 | else: |
1165 | # Feed selected from the menu. Change screen. |
1166 | - feed = Feed(self.menu.get_current_menuitem().get_userdata()) |
1167 | + feed = self.menu.selected_userdata |
1168 | kwargs = { 'feed' : feed } |
1169 | self.callback("feed", kwargs) |
1170 | |
1171 | - def handle_user_event(self, event): |
1172 | + def handle_user_event(self, event=None): |
1173 | '''Handle screen specific user events unless the library is empty.''' |
1174 | if self.library.is_empty() or len(self.library.get_feeds()) == 0: |
1175 | return |
1176 | else: |
1177 | Screen.handle_user_event(self, event) |
1178 | |
1179 | + def _display_selected_feed(self, actor=None): |
1180 | + '''Update of the list indicator and the description label''' |
1181 | + self.li.set_current(self.menu.selected_index + 1) |
1182 | + feed = self.menu.selected_userdata |
1183 | + self.description.set_text(self.parser.strip_tags(feed.description)) |
1184 | + |
1185 | + def _menu_activation(self, actor=None): |
1186 | + '''Handle the menu activation''' |
1187 | + self.menu.active = True |
1188 | + self.context_menu.active = False |
1189 | + self.li.show_position() |
1190 | + self.description.show() |
1191 | + |
1192 | + def _context_menu_activation(self, actor=None): |
1193 | + '''Handle the context menu activation''' |
1194 | + self.menu.active = False |
1195 | + self.context_menu.active = True |
1196 | + self.li.hide_position() |
1197 | + self.description.hide() |
1198 | + |
1199 | |
1200 | === modified file 'entertainerlib/frontend/gui/screens/screen.py' |
1201 | --- entertainerlib/frontend/gui/screens/screen.py 2009-05-16 16:40:48 +0000 |
1202 | +++ entertainerlib/frontend/gui/screens/screen.py 2009-05-28 20:15:47 +0000 |
1203 | @@ -112,7 +112,7 @@ |
1204 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
1205 | pass |
1206 | |
1207 | - def _handle_select(self): |
1208 | + def _handle_select(self, event=None): |
1209 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
1210 | pass |
1211 | |
1212 | @@ -134,6 +134,7 @@ |
1213 | if not self.has_tabs: |
1214 | raise ScreenException('This screen does not support tabs.') |
1215 | |
1216 | + tab.tab_group = self.tab_group |
1217 | self.tab_group.add_tab(tab) |
1218 | # XXX: laymansterms - figure this out, why do we need to add directly |
1219 | # to the screen instead of the tab group, why is the positioning |
1220 | |
1221 | === modified file 'entertainerlib/frontend/gui/screens/tv_episodes.py' |
1222 | --- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-01 11:15:57 +0000 |
1223 | +++ entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-21 22:22:53 +0000 |
1224 | @@ -5,14 +5,12 @@ |
1225 | import pango |
1226 | |
1227 | from entertainerlib.frontend.gui.screens.screen import Screen |
1228 | -from entertainerlib.frontend.gui.user_event import UserEvent |
1229 | from entertainerlib.frontend.gui.widgets.eyecandy_texture import ( |
1230 | EyeCandyTexture) |
1231 | from entertainerlib.frontend.gui.widgets.label import Label |
1232 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
1233 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
1234 | from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea |
1235 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
1236 | |
1237 | class TvEpisodes(Screen): |
1238 | '''Screen contains list of all episodes of one specific season.''' |
1239 | @@ -32,58 +30,43 @@ |
1240 | self.add(screen_title) |
1241 | |
1242 | self.scroll_area = None |
1243 | - self.episode_menu = None |
1244 | self.title = None |
1245 | self.thumb = None |
1246 | - self._create_episode_menu() |
1247 | + self.menu = self._create_episode_menu() |
1248 | + self.add(self.menu) |
1249 | self._create_episode_info_box() |
1250 | |
1251 | #List indicator |
1252 | self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) |
1253 | - self.li.set_maximum(self.episode_menu.get_number_of_items()) |
1254 | + self.li.set_maximum( |
1255 | + len(self.tv_series.get_episodes_from_season(self.season_number))) |
1256 | self.add(self.li) |
1257 | |
1258 | - # Add the additional actions that are needed but not handled by default |
1259 | - self.event_handlers.update({ |
1260 | - UserEvent.NAVIGATE_FIRST_PAGE : self._handle_first_page, |
1261 | - UserEvent.NAVIGATE_LAST_PAGE : self._handle_last_page, |
1262 | - UserEvent.NAVIGATE_PREVIOUS_PAGE : self._handle_previous_page, |
1263 | - UserEvent.NAVIGATE_NEXT_PAGE : self._handle_next_page |
1264 | - }) |
1265 | + self.menu.connect("moved", self._update_episode_info) |
1266 | + self.menu.connect("selected", self._handle_select) |
1267 | + self.menu.connect("activated", self._on_menu_activated) |
1268 | |
1269 | def _create_episode_menu(self): |
1270 | - """ |
1271 | - Create a list of available seasons |
1272 | - """ |
1273 | - self.episode_menu = TextMenu(self.theme, self.config.show_effects()) |
1274 | - self.episode_menu.set_row_count(1) |
1275 | - #self.episode_menu.set_visible_column_count(10) |
1276 | - self.episode_menu.set_item_size(self.get_abs_x(0.4393), |
1277 | - self.get_abs_y(0.0781)) |
1278 | - self.episode_menu.set_position(self.get_abs_x(0.4978), |
1279 | - self.get_abs_y(0.1563)) |
1280 | + """Create a list of available seasons.""" |
1281 | + menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781) |
1282 | |
1283 | - # Create menu items based on MusicLibrary |
1284 | episodes = self.tv_series.get_episodes_from_season(self.season_number) |
1285 | - for episode in episodes: |
1286 | - item = TextMenuItem(0.4393, 0.0781, |
1287 | - str(episode.get_episode_number()) + ". " + episode.get_title()) |
1288 | - item.set_userdata(episode) |
1289 | - self.episode_menu.add_actor(item) |
1290 | - |
1291 | - self.episode_menu.set_active(True) |
1292 | - self.add(self.episode_menu) |
1293 | + episodes_list = [[_("%(num)d. %(title)s") % \ |
1294 | + {'num': episode.get_episode_number(), 'title': episode.get_title()}, |
1295 | + None, episode] for episode in episodes] |
1296 | + menu.async_add(episodes_list) |
1297 | + |
1298 | + menu.active = True |
1299 | + |
1300 | + return menu |
1301 | |
1302 | def _create_thumbnail_texture(self): |
1303 | - """ |
1304 | - Create a thumbnail texture. This is called as menu is scrolled |
1305 | - """ |
1306 | + """Create a thumbnail texture. This is called as menu is scrolled.""" |
1307 | if self.thumb: |
1308 | self.thumb.hide() |
1309 | |
1310 | # Thumbnail. Use cover art if thumbnail doesn't exist |
1311 | - menu_item = self.episode_menu.get_current_menuitem() |
1312 | - thumbnail = menu_item.get_userdata().get_thumbnail_url() |
1313 | + thumbnail = self.menu.selected_userdata.get_thumbnail_url() |
1314 | if(thumbnail is not None): |
1315 | pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail) |
1316 | thumb_width = 0.2928 |
1317 | @@ -115,7 +98,7 @@ |
1318 | |
1319 | # Title |
1320 | self.title = Label(0.04, "title", 0.05, 0.55, |
1321 | - self.episode_menu.get_current_menuitem().get_userdata().get_title(), |
1322 | + self.menu.selected_userdata.get_title(), |
1323 | font_weight="bold") |
1324 | self.title.set_ellipsize(pango.ELLIPSIZE_END) |
1325 | self.title.width = 0.4 |
1326 | @@ -123,79 +106,62 @@ |
1327 | |
1328 | # Plot |
1329 | plot = Label(0.029, "subtitle", 0, 0, |
1330 | - self.episode_menu.get_current_menuitem().get_userdata().get_plot()) |
1331 | + self.menu.selected_userdata.get_plot()) |
1332 | plot.width = 0.4 |
1333 | |
1334 | - self.scroll_area = ScrollArea(0.4, 0.3516, 0.05, 0.63, plot) |
1335 | + self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot) |
1336 | + self.scroll_area.connect("activated", self._on_scroll_area_activated) |
1337 | self.add(self.scroll_area) |
1338 | |
1339 | - def _update_episode_info_box(self, episode): |
1340 | - """ |
1341 | - Update episode infobox |
1342 | - @param episode: Display information from this episode |
1343 | - """ |
1344 | + def _update_episode_info(self, event=None): |
1345 | + '''Update information from this episode.''' |
1346 | + self.li.set_current(self.menu.selected_index + 1) |
1347 | + |
1348 | self._create_thumbnail_texture() |
1349 | - self.title.set_text(episode.get_title()) |
1350 | + self.title.set_text(self.menu.selected_userdata.get_title()) |
1351 | self.title.width = 0.4 |
1352 | |
1353 | plot = Label(0.029, "subtitle", 0, 0, |
1354 | - self.episode_menu.get_current_menuitem().get_userdata().get_plot()) |
1355 | + self.menu.selected_userdata.get_plot()) |
1356 | plot.width = 0.4 |
1357 | self.scroll_area.set_content(plot) |
1358 | |
1359 | - def _move_menu(self, menu_direction): |
1360 | - '''Move the menu in the given direction.''' |
1361 | - if menu_direction == TextMenu.UP: |
1362 | - scroll_direction = self.scroll_area.scroll_up |
1363 | - elif menu_direction == TextMenu.DOWN: |
1364 | - scroll_direction = self.scroll_area.scroll_down |
1365 | - |
1366 | - if self.episode_menu.is_active(): |
1367 | - self.episode_menu.move(menu_direction) |
1368 | - self.li.set_current(self.episode_menu.get_current_position() + 1) |
1369 | - self._update_episode_info_box( |
1370 | - self.episode_menu.get_current_menuitem().get_userdata()) |
1371 | - else: |
1372 | - scroll_direction() |
1373 | - |
1374 | def _handle_up(self): |
1375 | '''Handle UserEvent.NAVIGATE_UP.''' |
1376 | - self._move_menu(TextMenu.UP) |
1377 | + if self.menu.active: |
1378 | + self.menu.up() |
1379 | + else: |
1380 | + self.scroll_area.scroll_up() |
1381 | |
1382 | def _handle_down(self): |
1383 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
1384 | - self._move_menu(TextMenu.DOWN) |
1385 | + if self.menu.active: |
1386 | + self.menu.down() |
1387 | + else: |
1388 | + self.scroll_area.scroll_down() |
1389 | |
1390 | def _handle_left(self): |
1391 | '''Handle UserEvent.NAVIGATE_LEFT.''' |
1392 | - self.episode_menu.set_active(False) |
1393 | - self.scroll_area.set_active(True) |
1394 | + self.menu.active = False |
1395 | + self.scroll_area.active = True |
1396 | |
1397 | def _handle_right(self): |
1398 | '''Handle UserEvent.NAVIGATE_RIGHT.''' |
1399 | - self.episode_menu.set_active(True) |
1400 | - self.scroll_area.set_active(False) |
1401 | + self.menu.active = True |
1402 | + self.scroll_area.active = False |
1403 | |
1404 | - def _handle_select(self): |
1405 | + def _handle_select(self, event=None): |
1406 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
1407 | - episode = self.episode_menu.get_current_menuitem().get_userdata() |
1408 | + episode = self.menu.selected_userdata |
1409 | self.media_player.set_media(episode) |
1410 | self.media_player.play() |
1411 | self.callback("video_osd") |
1412 | |
1413 | - def _handle_first_page(self): |
1414 | - '''Handle UserEvent.NAVIGATE_FIRST_PAGE.''' |
1415 | - self.scroll_area.scroll_to_top() |
1416 | - |
1417 | - def _handle_last_page(self): |
1418 | - '''Handle UserEvent.NAVIGATE_LAST_PAGE.''' |
1419 | - self.scroll_area.scroll_to_bottom() |
1420 | - |
1421 | - def _handle_previous_page(self): |
1422 | - '''Handle UserEvent.NAVIGATE_PREVIOUS_PAGE.''' |
1423 | - self.scroll_area.scroll_page_up() |
1424 | - |
1425 | - def _handle_next_page(self): |
1426 | - '''Handle UserEvent.NAVIGATE_NEXT_PAGE.''' |
1427 | - self.scroll_area.scroll_page_down() |
1428 | + def _on_menu_activated(self, event=None): |
1429 | + '''Handle menu activation.''' |
1430 | + self.scroll_area.active = False |
1431 | + |
1432 | + def _on_scroll_area_activated(self, event=None): |
1433 | + '''Handle scroll_area activation.''' |
1434 | + self.menu.active = False |
1435 | |
1436 | |
1437 | === modified file 'entertainerlib/frontend/gui/screens/tv_series.py' |
1438 | --- entertainerlib/frontend/gui/screens/tv_series.py 2009-07-01 11:15:57 +0000 |
1439 | +++ entertainerlib/frontend/gui/screens/tv_series.py 2009-07-12 07:57:05 +0000 |
1440 | @@ -9,7 +9,6 @@ |
1441 | from entertainerlib.frontend.gui.widgets.label import Label |
1442 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
1443 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
1444 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
1445 | |
1446 | class TvSeries(Screen): |
1447 | '''Screen that contains all seasons of one TV series.''' |
1448 | @@ -26,37 +25,35 @@ |
1449 | self.add(screen_title) |
1450 | |
1451 | self.art = None |
1452 | - self.season_menu = None |
1453 | + self.menu = None |
1454 | self._create_series_cover_texture() |
1455 | - self._create_season_menu() |
1456 | + self.menu = self._create_season_menu() |
1457 | + self.add(self.menu) |
1458 | |
1459 | #List indicator |
1460 | self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL) |
1461 | - self.li.set_maximum(self.season_menu.get_number_of_items()) |
1462 | + self.li.set_maximum(len(self.tv_series.get_seasons())) |
1463 | self.add(self.li) |
1464 | |
1465 | + self.menu.connect("moved", self._update_season_info) |
1466 | + self.menu.connect("selected", self._handle_select) |
1467 | + |
1468 | def _create_season_menu(self): |
1469 | """ |
1470 | Create a list of available seasons |
1471 | """ |
1472 | - self.season_menu = TextMenu(self.theme, self.config.show_effects()) |
1473 | - self.season_menu.set_row_count(1) |
1474 | - self.season_menu.set_item_size(self.get_abs_x(0.4393), |
1475 | - self.get_abs_y(0.0781)) |
1476 | - self.season_menu.set_position(self.get_abs_x(0.4978), |
1477 | - self.get_abs_y(0.1563)) |
1478 | + menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781) |
1479 | |
1480 | - # Create menu items based on MusicLibrary |
1481 | seasons = self.tv_series.get_seasons() |
1482 | seasons.sort() |
1483 | - for season in seasons: |
1484 | - item = TextMenuItem(0.4393, 0.0781, |
1485 | - _("Season %(num)s") % {'num': season}) |
1486 | - item.set_userdata(season) |
1487 | - self.season_menu.add_actor(item) |
1488 | - |
1489 | - self.season_menu.set_active(True) |
1490 | - self.add(self.season_menu) |
1491 | + |
1492 | + seasons_list = [[_("Season %(num)s") % {'num': season}, None, season] \ |
1493 | + for season in seasons] |
1494 | + menu.async_add(seasons_list) |
1495 | + |
1496 | + menu.active = True |
1497 | + |
1498 | + return menu |
1499 | |
1500 | def _create_series_cover_texture(self): |
1501 | """ |
1502 | @@ -72,22 +69,21 @@ |
1503 | self.art = EyeCandyTexture(0.16, 0.15, 0.2196, 0.5859, pixbuf) |
1504 | self.add(self.art) |
1505 | |
1506 | - def _move_menu(self, menu_direction): |
1507 | - '''Move the menu in the given direction.''' |
1508 | - self.season_menu.move(menu_direction) |
1509 | - self.li.set_current(self.season_menu.get_current_position() + 1) |
1510 | + def _update_season_info(self, event=None): |
1511 | + '''Update the ListIndicator.''' |
1512 | + self.li.set_current(self.menu.selected_index + 1) |
1513 | |
1514 | def _handle_up(self): |
1515 | '''Handle UserEvent.NAVIGATE_UP.''' |
1516 | - self._move_menu(TextMenu.UP) |
1517 | + self.menu.up() |
1518 | |
1519 | def _handle_down(self): |
1520 | '''Handle UserEvent.NAVIGATE_DOWN.''' |
1521 | - self._move_menu(TextMenu.DOWN) |
1522 | + self.menu.down() |
1523 | |
1524 | - def _handle_select(self): |
1525 | + def _handle_select(self, event=None): |
1526 | '''Handle UserEvent.NAVIGATE_SELECT.''' |
1527 | - season = self.season_menu.get_current_menuitem().get_userdata() |
1528 | + season = self.menu.selected_userdata |
1529 | kwargs = { 'tv_series' : self.tv_series, 'season_number' : season } |
1530 | self.callback("tv_episodes", kwargs) |
1531 | |
1532 | |
1533 | === modified file 'entertainerlib/frontend/gui/tabs/albums_tab.py' |
1534 | --- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-05-14 03:22:31 +0000 |
1535 | +++ entertainerlib/frontend/gui/tabs/albums_tab.py 2009-07-14 11:06:21 +0000 |
1536 | @@ -2,18 +2,14 @@ |
1537 | '''Albums tab which displays albums and allows users to select them''' |
1538 | # pylint: disable-msg=W1001 |
1539 | |
1540 | -import clutter |
1541 | -import gobject |
1542 | import pango |
1543 | |
1544 | from entertainerlib.frontend.gui.tabs.tab import Tab |
1545 | from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
1546 | -from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem |
1547 | from entertainerlib.frontend.gui.widgets.label import Label |
1548 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
1549 | from entertainerlib.frontend.gui.widgets.loading_animation import ( |
1550 | LoadingAnimation) |
1551 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
1552 | |
1553 | class AlbumsTab(Tab): |
1554 | '''Tab to show album listings''' |
1555 | @@ -28,48 +24,28 @@ |
1556 | self.add(self.throbber) |
1557 | |
1558 | if len(albums) < 4: |
1559 | - # XXX: laymansterms - This is "less than elegant." I'm not fixing |
1560 | - # ImageMenu right now so we can't get rid of tex_size_x and y yet. |
1561 | - # We really need to eliminate the concept tex_size_y, but it will |
1562 | - # have to wait for now. |
1563 | x_percent = 0.2928 |
1564 | - tex_size_x = self.get_abs_x(x_percent) |
1565 | - tex_size_y = self.get_abs_y(0.5208) |
1566 | - row_count = 1 |
1567 | - visible = 3 |
1568 | + visible_rows = 1 |
1569 | + visible_cols = 3 |
1570 | elif len(albums) < 13: |
1571 | x_percent = 0.1464 |
1572 | - tex_size_x = self.get_abs_x(x_percent) |
1573 | - tex_size_y = self.get_abs_y(0.2604) |
1574 | - row_count = 2 |
1575 | - visible = 6 |
1576 | + visible_rows = 2 |
1577 | + visible_cols = 6 |
1578 | else: |
1579 | x_percent = 0.1098 |
1580 | - tex_size_x = self.get_abs_x(x_percent) |
1581 | - tex_size_y = self.get_abs_y(0.1953) |
1582 | - row_count = 3 |
1583 | - visible = 8 |
1584 | + visible_rows = 3 |
1585 | + visible_cols = 8 |
1586 | |
1587 | # Create albums menu |
1588 | - self.menu = ImageMenu(self.theme) |
1589 | - self.menu.set_row_count(row_count) |
1590 | - self.menu.set_visible_column_count(visible) |
1591 | - self.menu.set_item_size(tex_size_x, tex_size_y) |
1592 | - |
1593 | - # Default texture |
1594 | - self.default_cover = Texture(self.theme.getImage("default_album_art")) |
1595 | - |
1596 | - self._async_menu_build(albums, x_percent) |
1597 | - self._selected_album = self.menu.get_current_menuitem().get_userdata() |
1598 | - |
1599 | - self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.1823)) |
1600 | - self.menu.show() |
1601 | - |
1602 | - if self.config.show_effects(): |
1603 | - self.menu.set_animate(True) |
1604 | - |
1605 | + self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent)) |
1606 | + self.menu.visible_rows = visible_rows |
1607 | + self.menu.visible_cols = visible_cols |
1608 | + self.menu.items_per_col = self.menu.visible_rows |
1609 | self.add(self.menu) |
1610 | |
1611 | + albums_list = [[album.get_album_art_url(), album] for album in albums] |
1612 | + self.menu.async_add_albums(albums_list) |
1613 | + |
1614 | self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, |
1615 | ListIndicator.HORIZONTAL) |
1616 | self.li.set_maximum(len(albums)) |
1617 | @@ -92,102 +68,79 @@ |
1618 | |
1619 | self.connect('activated', self._on_activated) |
1620 | self.connect('deactivated', self._on_deactivated) |
1621 | - |
1622 | - def _async_menu_build(self, albums, item_size_percent): |
1623 | - """ |
1624 | - This function is called as an async callback. It is also a recursive |
1625 | - function. This allows us to load menu items in the background without |
1626 | - blocking the user interface. |
1627 | - """ |
1628 | - |
1629 | - # Check if we should stop recursive calls (menu is complete) |
1630 | - if len(albums) > 0: |
1631 | - album = albums[0] |
1632 | - if album.has_album_art(): |
1633 | - texture = Texture(album.get_album_art_url()) |
1634 | - else: |
1635 | - texture = clutter.CloneTexture(self.default_cover) |
1636 | - |
1637 | - item = ImageMenuItem(item_size_percent, 1, texture) |
1638 | - item.set_userdata(album) |
1639 | - self.menu.add_actor(item) |
1640 | - # Recursive call, remove first album from the list |
1641 | - gobject.timeout_add(25, self._async_menu_build, albums[1:], |
1642 | - item_size_percent) |
1643 | - return False |
1644 | - else: |
1645 | - self.throbber.hide() |
1646 | - # Done loading the albums, notify gobject to stop calling menu build |
1647 | - return False |
1648 | - |
1649 | - def _get_selected_album(self): |
1650 | - '''Get the currently selected album from the menu.''' |
1651 | - return self.menu.get_current_menuitem().get_userdata() |
1652 | - |
1653 | - selected_album = property(_get_selected_album) |
1654 | + self.menu.connect("moved", self._update_album_info) |
1655 | + self.menu.connect("selected", self._handle_select) |
1656 | + self.menu.connect("activated", self._on_activated) |
1657 | + self.menu.connect("filled", self._on_menu_filled) |
1658 | |
1659 | def can_activate(self): |
1660 | '''Albums tab will always be created from an existing artist with at |
1661 | least one album.''' |
1662 | return True |
1663 | |
1664 | - def _update_album_info(self): |
1665 | + def _update_album_info(self, event=None): |
1666 | '''Update the album information labels.''' |
1667 | if self.active: |
1668 | - album = self.menu.get_current_menuitem().get_userdata() |
1669 | + album = self.menu.selected_userdata |
1670 | self.album_title.set_text(album.get_title()) |
1671 | self.album_artist.set_text(album.get_artist()) |
1672 | self.album_tracks.set_text(_("%(total)s tracks") % \ |
1673 | {'total': album.get_number_of_tracks()}) |
1674 | + self.li.show() |
1675 | + self.li.set_current(self.menu.selected_index + 1) |
1676 | else: |
1677 | self.album_title.set_text("") |
1678 | self.album_artist.set_text("") |
1679 | self.album_tracks.set_text("") |
1680 | - |
1681 | - def _update_menu(self, direction): |
1682 | - '''Update the menu based on the provided menu direction.''' |
1683 | - self.menu.move(direction) |
1684 | - self._update_album_info() |
1685 | - self.li.set_current(self.menu.get_current_position() + 1) |
1686 | - return False |
1687 | + self.li.hide() |
1688 | |
1689 | def _handle_up(self): |
1690 | '''Handle the up user event.''' |
1691 | - if self.menu.get_cursor_position()[1] == 0: |
1692 | + if self.menu.on_top: |
1693 | return True # Move control back to tab bar |
1694 | else: |
1695 | - return self._update_menu(self.menu.UP) |
1696 | + self.menu.up() |
1697 | + return False |
1698 | |
1699 | def _handle_down(self): |
1700 | '''Handle the down user event.''' |
1701 | - return self._update_menu(self.menu.DOWN) |
1702 | + self.menu.down() |
1703 | + return False |
1704 | |
1705 | def _handle_left(self): |
1706 | '''Handle the left user event.''' |
1707 | - return self._update_menu(self.menu.LEFT) |
1708 | + self.menu.left() |
1709 | + return False |
1710 | |
1711 | def _handle_right(self): |
1712 | '''Handle the right user event.''' |
1713 | - return self._update_menu(self.menu.RIGHT) |
1714 | + self.menu.right() |
1715 | + return False |
1716 | |
1717 | - def _handle_select(self): |
1718 | + def _handle_select(self, event=None): |
1719 | '''Handle the select user event.''' |
1720 | - album = self.menu.get_current_menuitem().get_userdata() |
1721 | + album = self.menu.selected_userdata |
1722 | kwargs = { 'album' : album } |
1723 | self.callback("album", kwargs) |
1724 | return False |
1725 | |
1726 | def _on_activated(self, event=None): |
1727 | '''Tab activated.''' |
1728 | + if self.tab_group is not None: |
1729 | + self.tab_group.active = False |
1730 | + self.menu.active = True |
1731 | + self.active = True |
1732 | self._update_album_info() |
1733 | - self.menu.set_active(True) |
1734 | - self.menu.set_opacity(255) |
1735 | return False |
1736 | |
1737 | def _on_deactivated(self, event=None): |
1738 | '''Tab deactivated.''' |
1739 | + self.active = False |
1740 | + self.menu.active = False |
1741 | self._update_album_info() |
1742 | - self.menu.set_active(False) |
1743 | - self.menu.set_opacity(128) |
1744 | return False |
1745 | |
1746 | + def _on_menu_filled(self, event=None): |
1747 | + '''Handles filled event.''' |
1748 | + self.throbber.hide() |
1749 | + |
1750 | |
1751 | === modified file 'entertainerlib/frontend/gui/tabs/artists_tab.py' |
1752 | --- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-05-14 03:22:31 +0000 |
1753 | +++ entertainerlib/frontend/gui/tabs/artists_tab.py 2009-07-16 20:31:22 +0000 |
1754 | @@ -2,8 +2,6 @@ |
1755 | '''Artists tab for the music screen''' |
1756 | # pylint: disable-msg=W1001 |
1757 | |
1758 | -import clutter |
1759 | -import gobject |
1760 | import pango |
1761 | |
1762 | from entertainerlib.frontend.gui.tabs.tab import Tab |
1763 | @@ -12,7 +10,6 @@ |
1764 | from entertainerlib.frontend.gui.widgets.loading_animation import ( |
1765 | LoadingAnimation) |
1766 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
1767 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
1768 | |
1769 | class ArtistsTab(Tab): |
1770 | '''Tab for the music screen to show artist listings''' |
1771 | @@ -27,19 +24,17 @@ |
1772 | self.throbber.show() |
1773 | self.add(self.throbber) |
1774 | |
1775 | - self.menu = TextMenu(self.config.theme, self.config.show_effects()) |
1776 | - self.menu.set_item_size(self.get_abs_x(0.293), self.get_abs_y(0.078)) |
1777 | - cursor = clutter.Rectangle() |
1778 | - cursor.set_color((0, 0, 0, 0)) |
1779 | - self.menu.set_cursor(cursor) |
1780 | - self.menu.set_orientation(TextMenu.HORIZONTAL) |
1781 | - self.menu.set_row_count(7) |
1782 | - self.menu.set_visible_column_count(3) |
1783 | - self.menu.set_position(self.get_abs_x(0.057), self.get_abs_y(0.208)) |
1784 | - if self.config.show_effects(): |
1785 | - self.menu.set_animate(True) |
1786 | + self.menu = TextMenu(0.057, 0.208, 0.293, 0.078) |
1787 | + self.menu.items_per_row = 3 |
1788 | + self.menu.visible_rows = 7 |
1789 | + self.menu.visible_cols = 3 |
1790 | + self.menu.active = False |
1791 | + self.menu.cursor = None |
1792 | self.add(self.menu) |
1793 | |
1794 | + artists_list = [[artist, None, artist] for artist in artists] |
1795 | + self.menu.async_add_artists(artists_list) |
1796 | + |
1797 | # Create artist label |
1798 | self.artist_title = Label(0.0416, "title", 0.22, 0.794, "") |
1799 | self.artist_title.set_ellipsize(pango.ELLIPSIZE_END) |
1800 | @@ -54,107 +49,85 @@ |
1801 | |
1802 | # Create artis menu list indicator |
1803 | self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, |
1804 | - ListIndicator.HORIZONTAL) |
1805 | + ListIndicator.VERTICAL) |
1806 | self.li.set_maximum(len(artists)) |
1807 | self.add(self.li) |
1808 | |
1809 | - self._async_menu_build(artists) |
1810 | - self._selected_artist = self.menu.get_current_menuitem().get_userdata() |
1811 | - |
1812 | self.connect('activated', self._on_activated) |
1813 | self.connect('deactivated', self._on_deactivated) |
1814 | - |
1815 | - def _async_menu_build(self, artists): |
1816 | - """ |
1817 | - This function is called as an async callback. It is also a recursive |
1818 | - function. This allows us to load menu items in the background without |
1819 | - blocking the user interface. |
1820 | - """ |
1821 | - |
1822 | - # Check if we should stop recursive calls (menu is complete) |
1823 | - if len(artists) > 0: |
1824 | - artist = artists[0] |
1825 | - item = TextMenuItem(0.293, 0.104, artist) |
1826 | - item.justified = True |
1827 | - item.set_userdata(artist) |
1828 | - self.menu.add_actor(item) |
1829 | - # Recursive call, remove first artist from the list |
1830 | - gobject.timeout_add(15, self._async_menu_build, artists[1:]) |
1831 | - return False |
1832 | - else: |
1833 | - self.throbber.hide() |
1834 | - # Done loading artists, notify gobject to stop calling menu build |
1835 | - return False |
1836 | - |
1837 | - def _get_selected_artist(self): |
1838 | - '''Get the currently selected artist from the menu.''' |
1839 | - return self.menu.get_current_menuitem().get_userdata() |
1840 | - |
1841 | - selected_artist = property(_get_selected_artist) |
1842 | + self.menu.connect("moved", self._update_artist_info) |
1843 | + self.menu.connect("selected", self._handle_select) |
1844 | + self.menu.connect("activated", self._on_activated) |
1845 | + self.menu.connect("filled", self._on_menu_filled) |
1846 | |
1847 | def can_activate(self): |
1848 | '''Albums tab will always be created from an existing artist with at |
1849 | least one album.''' |
1850 | return True |
1851 | |
1852 | - def _update_artist_info(self): |
1853 | + def _update_artist_info(self, event=None): |
1854 | '''Update the artist information labels''' |
1855 | if self.active: |
1856 | - artist = self.menu.get_current_menuitem().get_userdata() |
1857 | + artist = self.menu.selected_userdata |
1858 | self.artist_title.set_text(artist) |
1859 | self.artist_albums.set_text(_("%(albums)d albums") % |
1860 | {'albums': self.library.number_of_albums_by_artist(artist)}) |
1861 | self.artist_tracks.set_text(_("%(tracks)d tracks") % |
1862 | {'tracks': self.library.number_of_tracks_by_artist(artist)}) |
1863 | + self.li.show() |
1864 | + self.li.set_current(self.menu.selected_index + 1) |
1865 | else: |
1866 | self.artist_title.set_text("") |
1867 | self.artist_albums.set_text("") |
1868 | self.artist_tracks.set_text("") |
1869 | - |
1870 | - def _update_menu(self, direction): |
1871 | - '''Update the menu based on the provided menu direction.''' |
1872 | - self.menu.move(direction) |
1873 | - self._update_artist_info() |
1874 | - self.li.set_current(self.menu.get_current_position() + 1) |
1875 | - return False |
1876 | + self.li.hide() |
1877 | |
1878 | def _handle_up(self): |
1879 | '''Handle the up user event.''' |
1880 | - if self.menu.get_cursor_position()[1] == 0: |
1881 | + if self.menu.on_top: |
1882 | return True # Move control back to tab bar |
1883 | else: |
1884 | - return self._update_menu(self.menu.UP) |
1885 | + self.menu.up() |
1886 | + return False |
1887 | |
1888 | def _handle_down(self): |
1889 | '''Handle the down user event.''' |
1890 | - return self._update_menu(self.menu.DOWN) |
1891 | + self.menu.down() |
1892 | + return False |
1893 | |
1894 | def _handle_left(self): |
1895 | '''Handle the left user event.''' |
1896 | - return self._update_menu(self.menu.LEFT) |
1897 | + self.menu.left() |
1898 | + return False |
1899 | |
1900 | def _handle_right(self): |
1901 | '''Handle the right user event.''' |
1902 | - return self._update_menu(self.menu.RIGHT) |
1903 | + self.menu.right() |
1904 | + return False |
1905 | |
1906 | - def _handle_select(self): |
1907 | + def _handle_select(self, event=None): |
1908 | '''Handle the select user event.''' |
1909 | - artist = self.menu.get_current_menuitem().get_userdata() |
1910 | + artist = self.menu.selected_userdata |
1911 | kwargs = { 'artist' : artist } |
1912 | self.callback("artist", kwargs) |
1913 | return False |
1914 | |
1915 | def _on_activated(self, event=None): |
1916 | '''Tab activated.''' |
1917 | + if self.tab_group is not None: |
1918 | + self.tab_group.active = False |
1919 | + self.menu.active = True |
1920 | + self.active = True |
1921 | self._update_artist_info() |
1922 | - self.menu.set_active(True) |
1923 | - self.menu.set_opacity(255) |
1924 | return False |
1925 | |
1926 | def _on_deactivated(self, event=None): |
1927 | '''Tab deactivated.''' |
1928 | + self.active = False |
1929 | + self.menu.active = False |
1930 | self._update_artist_info() |
1931 | - self.menu.set_active(False) |
1932 | - self.menu.set_opacity(128) |
1933 | - return False |
1934 | + |
1935 | + def _on_menu_filled(self, event=None): |
1936 | + '''Handles filled event.''' |
1937 | + self.throbber.hide() |
1938 | |
1939 | |
1940 | === modified file 'entertainerlib/frontend/gui/tabs/lyrics_tab.py' |
1941 | --- entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-05-14 03:22:31 +0000 |
1942 | +++ entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-07-22 20:46:29 +0000 |
1943 | @@ -1,6 +1,7 @@ |
1944 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
1945 | '''Lyrics tab for the audio play screen''' |
1946 | |
1947 | +import clutter |
1948 | import pango |
1949 | |
1950 | from entertainerlib.frontend.gui.tabs.tab import Tab |
1951 | @@ -26,7 +27,8 @@ |
1952 | lyrics.set_line_wrap_mode(pango.WRAP_WORD) |
1953 | lyrics.width = 0.366 |
1954 | |
1955 | - self.lyrics_area = ScrollArea(0.4, 0.57, 0.5, 0.23, lyrics) |
1956 | + self.lyrics_area = ScrollArea(0.5, 0.23, 0.4, 0.57, lyrics) |
1957 | + self.lyrics_area.connect("activated", self._on_activated) |
1958 | self.add(self.lyrics_area) |
1959 | |
1960 | self.connect('activated', self._on_activated) |
1961 | @@ -39,24 +41,24 @@ |
1962 | self.track.fetch_lyrics(self._lyrics_search_callback) |
1963 | |
1964 | def _lyrics_search_callback(self, lyrics_text): |
1965 | - ''' |
1966 | - This function is called when lyrics search is over. |
1967 | - ''' |
1968 | + '''This function is called when lyrics search is over.''' |
1969 | self.throbber.hide() |
1970 | |
1971 | - # Save the results to help determine if the tab can activate |
1972 | + # Save the results to help determine if the tab can activate. |
1973 | self.lyrics_text = lyrics_text |
1974 | |
1975 | - if lyrics_text is None: |
1976 | - no_lyrics = Label(0.037, "title", 0.5, 0.5, |
1977 | + if lyrics_text == "": |
1978 | + no_lyrics = Label(0.037, "title", 0.7, 0.5, |
1979 | _("No lyrics found for this track")) |
1980 | + no_lyrics.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER) |
1981 | self.add(no_lyrics) |
1982 | else: |
1983 | lyrics = Label(0.037, "subtitle", 0, 0, lyrics_text) |
1984 | lyrics.set_line_wrap_mode(pango.WRAP_WORD) |
1985 | lyrics.width = 0.366 |
1986 | |
1987 | - self.lyrics_area = ScrollArea(0.4, 0.57, 0.5, 0.23, lyrics) |
1988 | + self.lyrics_area = ScrollArea(0.5, 0.23, 0.4, 0.57, lyrics) |
1989 | + self.lyrics_area.connect("activated", self._on_activated) |
1990 | self.add(self.lyrics_area) |
1991 | self.library.save_lyrics(self.track, lyrics_text) |
1992 | |
1993 | @@ -71,7 +73,7 @@ |
1994 | |
1995 | def _handle_up(self): |
1996 | '''Handle the up user event.''' |
1997 | - if self.lyrics_area.get_offset() == 0: |
1998 | + if self.lyrics_area.on_top: |
1999 | return True # Move control back to tab bar |
2000 | else: |
2001 | return self.lyrics_area.scroll_up() |
2002 | @@ -82,13 +84,15 @@ |
2003 | |
2004 | def _on_activated(self, event=None): |
2005 | '''Tab activated.''' |
2006 | - self.lyrics_area.set_active(True) |
2007 | - self.lyrics_area.set_opacity(255) |
2008 | + if self.tab_group is not None: |
2009 | + self.tab_group.active = False |
2010 | + self.lyrics_area.active = True |
2011 | + self.active = True |
2012 | return False |
2013 | |
2014 | def _on_deactivated(self, event=None): |
2015 | '''Tab deactivated.''' |
2016 | - self.lyrics_area.set_active(False) |
2017 | - self.lyrics_area.set_opacity(128) |
2018 | + self.active = False |
2019 | + self.lyrics_area.active = False |
2020 | return False |
2021 | |
2022 | |
2023 | === modified file 'entertainerlib/frontend/gui/tabs/movies_tab.py' |
2024 | --- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-05-14 03:22:31 +0000 |
2025 | +++ entertainerlib/frontend/gui/tabs/movies_tab.py 2009-07-14 11:06:21 +0000 |
2026 | @@ -1,17 +1,14 @@ |
2027 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
2028 | '''MoviesTab - MoviesTab is group of objects that are displayed on movie tab''' |
2029 | |
2030 | -import clutter |
2031 | -import gtk |
2032 | import pango |
2033 | |
2034 | from entertainerlib.frontend.gui.tabs.tab import Tab |
2035 | from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
2036 | -from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem |
2037 | from entertainerlib.frontend.gui.widgets.label import Label |
2038 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
2039 | -from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture |
2040 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
2041 | +from entertainerlib.frontend.gui.widgets.loading_animation import ( |
2042 | + LoadingAnimation) |
2043 | |
2044 | class MoviesTab(Tab): |
2045 | """ |
2046 | @@ -24,6 +21,7 @@ |
2047 | def __init__(self, video_library, move_to_new_screen_callback, |
2048 | name="movies", tab_title=_("Movies")): |
2049 | Tab.__init__(self, name, tab_title, move_to_new_screen_callback) |
2050 | + |
2051 | self.video_library = video_library |
2052 | self.theme = self.config.theme |
2053 | self.list_indicator = None |
2054 | @@ -35,7 +33,17 @@ |
2055 | if self.video_library.get_number_of_movies() == 0: |
2056 | self._create_empty_library_notice() |
2057 | else: |
2058 | - self._create_menu() |
2059 | + # Start the loading animation while the menu is loading |
2060 | + self.throbber = LoadingAnimation(0.1, 0.1) |
2061 | + self.throbber.show() |
2062 | + self.add(self.throbber) |
2063 | + |
2064 | + self.menu = self._create_menu() |
2065 | + self.add(self.menu) |
2066 | + self.menu.connect("moved", self._update_movie_info) |
2067 | + self.menu.connect("selected", self._handle_select) |
2068 | + self.menu.connect("activated", self._on_activated) |
2069 | + self.menu.connect("filled", self._on_menu_filled) |
2070 | |
2071 | self.connect('activated', self._on_activated) |
2072 | self.connect('deactivated', self._on_deactivated) |
2073 | @@ -67,40 +75,19 @@ |
2074 | the video library. |
2075 | """ |
2076 | #Create movie menu |
2077 | - self.menu = ImageMenu(self.theme) |
2078 | - self.menu.set_item_size(self.get_abs_x(0.1), self.get_abs_y(0.25)) |
2079 | - self.menu.set_item_spacing(self.get_abs_y(0.025)) |
2080 | - self.menu.set_visible_column_count(2) |
2081 | - self.menu.set_row_count(7) |
2082 | - self.menu.set_orientation(self.menu.VERTICAL) |
2083 | - |
2084 | - # Default texture |
2085 | - default_cover = Texture(self.theme.getImage("default_movie_art")) |
2086 | + menu = ImageMenu(0.06, 0.18, 0.12, 0.25) |
2087 | + menu.items_per_col = 2 |
2088 | + menu.visible_rows = 2 |
2089 | + menu.visible_cols = 7 |
2090 | |
2091 | movies = self.video_library.get_movies() |
2092 | - for movie in movies: |
2093 | - if movie.has_cover_art(): |
2094 | - pix_buffer = gtk.gdk.pixbuf_new_from_file( |
2095 | - movie.get_cover_art_url()) |
2096 | - texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer) |
2097 | - else: |
2098 | - texture = clutter.CloneTexture(default_cover) |
2099 | - item = ImageMenuItem(0.1, 1.41, texture) |
2100 | - item.set_userdata(movie) |
2101 | - self.menu.add_actor(item) |
2102 | - |
2103 | - self.menu.set_position(self.get_abs_x(0.11), self.get_abs_y(0.2)) |
2104 | - self.menu.show() |
2105 | - |
2106 | - if self.config.show_effects(): |
2107 | - self.menu.set_animate(True) |
2108 | - |
2109 | - self.add(self.menu) |
2110 | + movies_list = [[movie.get_cover_art_url(), movie] for movie in movies] |
2111 | + menu.async_add_videos(movies_list) |
2112 | |
2113 | # Create list indicator |
2114 | self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045, |
2115 | - ListIndicator.VERTICAL) |
2116 | - self.list_indicator.set_maximum(self.menu.get_number_of_items()) |
2117 | + ListIndicator.HORIZONTAL) |
2118 | + self.list_indicator.set_maximum(len(movies)) |
2119 | self.show() |
2120 | self.add(self.list_indicator) |
2121 | |
2122 | @@ -120,10 +107,12 @@ |
2123 | self.movie_plot.width = 0.7 |
2124 | self.add(self.movie_plot) |
2125 | |
2126 | - def _update_movie_info(self): |
2127 | + return menu |
2128 | + |
2129 | + def _update_movie_info(self, event=None): |
2130 | '''Update the movie information labels.''' |
2131 | if self.active: |
2132 | - movie = self.menu.get_current_menuitem().get_userdata() |
2133 | + movie = self.menu.selected_userdata |
2134 | genres = movie.get_genres() |
2135 | if len(genres) > 1: |
2136 | genre = genres[0] + "/" + genres[1] |
2137 | @@ -135,55 +124,61 @@ |
2138 | self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \ |
2139 | {'min': movie.get_runtime(), 'genre': genre}) |
2140 | self.movie_plot.set_text(movie.get_short_plot()) |
2141 | + self.list_indicator.show() |
2142 | + self.list_indicator.set_current(self.menu.selected_index + 1) |
2143 | else: |
2144 | self.movie_title.set_text("") |
2145 | self.movie_info.set_text("") |
2146 | self.movie_plot.set_text("") |
2147 | - |
2148 | - def _update_menu(self, direction): |
2149 | - '''Update the menu based on the provided menu direction.''' |
2150 | - self.menu.move(direction) |
2151 | - self._update_movie_info() |
2152 | - self.list_indicator.set_current(self.menu.get_current_position() + 1) |
2153 | - return False |
2154 | + self.list_indicator.hide() |
2155 | |
2156 | def _handle_up(self): |
2157 | '''Handle the up user event.''' |
2158 | - if self.menu.get_current_position() <= 6: |
2159 | + if self.menu.on_top: |
2160 | return True # Move control back to tab bar |
2161 | else: |
2162 | - return self._update_menu(self.menu.UP) |
2163 | + self.menu.up() |
2164 | + return False |
2165 | |
2166 | def _handle_down(self): |
2167 | '''Handle the down user event.''' |
2168 | - return self._update_menu(self.menu.DOWN) |
2169 | + self.menu.down() |
2170 | + return False |
2171 | |
2172 | def _handle_left(self): |
2173 | '''Handle the left user event.''' |
2174 | - return self._update_menu(self.menu.LEFT) |
2175 | + self.menu.left() |
2176 | + return False |
2177 | |
2178 | def _handle_right(self): |
2179 | '''Handle the right user event.''' |
2180 | - return self._update_menu(self.menu.RIGHT) |
2181 | + self.menu.right() |
2182 | + return False |
2183 | |
2184 | - def _handle_select(self): |
2185 | + def _handle_select(self, event=None): |
2186 | '''Handle the select user event.''' |
2187 | - movie = self.menu.get_current_menuitem().get_userdata() |
2188 | + movie = self.menu.selected_userdata |
2189 | kwargs = { 'movie' : movie } |
2190 | self.callback("movie", kwargs) |
2191 | return False |
2192 | |
2193 | def _on_activated(self, event=None): |
2194 | '''Tab activated.''' |
2195 | + if self.tab_group is not None: |
2196 | + self.tab_group.active = False |
2197 | + self.menu.active = True |
2198 | + self.active = True |
2199 | self._update_movie_info() |
2200 | - self.menu.set_active(True) |
2201 | - self.menu.set_opacity(255) |
2202 | return False |
2203 | |
2204 | def _on_deactivated(self, event=None): |
2205 | '''Tab deactivated.''' |
2206 | + self.active = False |
2207 | + self.menu.active = False |
2208 | self._update_movie_info() |
2209 | - self.menu.set_active(False) |
2210 | - self.menu.set_opacity(128) |
2211 | return False |
2212 | |
2213 | + def _on_menu_filled(self, event=None): |
2214 | + '''Handles filled event.''' |
2215 | + self.throbber.hide() |
2216 | + |
2217 | |
2218 | === modified file 'entertainerlib/frontend/gui/tabs/series_tab.py' |
2219 | --- entertainerlib/frontend/gui/tabs/series_tab.py 2009-05-14 03:22:31 +0000 |
2220 | +++ entertainerlib/frontend/gui/tabs/series_tab.py 2009-07-14 11:06:21 +0000 |
2221 | @@ -1,17 +1,14 @@ |
2222 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
2223 | '''SeriesTab - Group of objects that are displayed on tv-series tab''' |
2224 | |
2225 | -import clutter |
2226 | -import gtk |
2227 | import pango |
2228 | |
2229 | from entertainerlib.frontend.gui.tabs.tab import Tab |
2230 | from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
2231 | -from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem |
2232 | from entertainerlib.frontend.gui.widgets.label import Label |
2233 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
2234 | -from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture |
2235 | -from entertainerlib.frontend.gui.widgets.texture import Texture |
2236 | +from entertainerlib.frontend.gui.widgets.loading_animation import ( |
2237 | + LoadingAnimation) |
2238 | |
2239 | class SeriesTab(Tab): |
2240 | """ |
2241 | @@ -24,6 +21,7 @@ |
2242 | def __init__(self, video_library, move_to_new_screen_callback, |
2243 | name="series", tab_title=_("TV-Series")): |
2244 | Tab.__init__(self, name, tab_title, move_to_new_screen_callback) |
2245 | + |
2246 | self.video_library = video_library |
2247 | self.theme = self.config.theme |
2248 | self.list_indicator = None |
2249 | @@ -34,7 +32,17 @@ |
2250 | if self.video_library.get_number_of_tv_series() == 0: |
2251 | self._create_empty_library_notice() |
2252 | else: |
2253 | - self._create_menu() |
2254 | + # Start the loading animation while the menu is loading |
2255 | + self.throbber = LoadingAnimation(0.4, 0.1) |
2256 | + self.throbber.show() |
2257 | + self.add(self.throbber) |
2258 | + |
2259 | + self.menu = self._create_menu() |
2260 | + self.add(self.menu) |
2261 | + self.menu.connect("moved", self._update_serie_info) |
2262 | + self.menu.connect("selected", self._handle_select) |
2263 | + self.menu.connect("activated", self._on_activated) |
2264 | + self.menu.connect("filled", self._on_menu_filled) |
2265 | |
2266 | self.connect('activated', self._on_activated) |
2267 | self.connect('deactivated', self._on_deactivated) |
2268 | @@ -66,42 +74,19 @@ |
2269 | the video library. |
2270 | """ |
2271 | #Create TV-series menu |
2272 | - self.menu = ImageMenu(self.theme) |
2273 | - self.menu.set_item_size(self.get_abs_x(0.1), self.get_abs_y(0.25)) |
2274 | - self.menu.set_item_spacing(self.get_abs_y(0.025)) |
2275 | - self.menu.set_visible_column_count(2) |
2276 | - self.menu.set_row_count(7) |
2277 | - self.menu.set_orientation(self.menu.VERTICAL) |
2278 | - |
2279 | - # Default texture |
2280 | - default_cover = Texture(self.theme.getImage("default_movie_art")) |
2281 | - |
2282 | - all_series = self.video_library.get_tv_series() |
2283 | - for series in all_series: |
2284 | - if series.has_cover_art(): |
2285 | - pix_buffer = gtk.gdk.pixbuf_new_from_file( |
2286 | - series.get_cover_art_url()) |
2287 | - texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer) |
2288 | - else: |
2289 | - texture = clutter.CloneTexture(default_cover) |
2290 | - |
2291 | - item = ImageMenuItem(0.1, 1.41, texture) |
2292 | - item.set_userdata(series) |
2293 | - self.menu.add_actor(item) |
2294 | - |
2295 | - self.menu.set_position(self.get_abs_x(0.11), self.get_abs_y(0.2)) |
2296 | - self.menu.set_active(False) |
2297 | - self.menu.show() |
2298 | - |
2299 | - if self.config.show_effects(): |
2300 | - self.menu.set_animate(True) |
2301 | - |
2302 | - self.add(self.menu) |
2303 | + menu = ImageMenu(0.06, 0.18, 0.12, 0.25) |
2304 | + menu.items_per_col = 2 |
2305 | + menu.visible_rows = 2 |
2306 | + menu.visible_cols = 7 |
2307 | + |
2308 | + series = self.video_library.get_tv_series() |
2309 | + series_list = [[serie.get_cover_art_url(), serie] for serie in series] |
2310 | + menu.async_add_videos(series_list) |
2311 | |
2312 | # Create list indicator |
2313 | self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045, |
2314 | - ListIndicator.VERTICAL) |
2315 | - self.list_indicator.set_maximum(self.menu.get_number_of_items()) |
2316 | + ListIndicator.HORIZONTAL) |
2317 | + self.list_indicator.set_maximum(len(series)) |
2318 | self.list_indicator.show() |
2319 | self.add(self.list_indicator) |
2320 | |
2321 | @@ -115,64 +100,72 @@ |
2322 | self.series_info = Label(0.034, "subtitle", 0.2, 0.82, "") |
2323 | self.add(self.series_info) |
2324 | |
2325 | - def _update_series_info(self): |
2326 | + return menu |
2327 | + |
2328 | + def _update_serie_info(self, event=None): |
2329 | '''Update the series information labels.''' |
2330 | if self.active: |
2331 | - series = self.menu.get_current_menuitem().get_userdata() |
2332 | + series = self.menu.selected_userdata |
2333 | info = _("%(season)d Seasons\n%(episode)d Episodes") % {'season': |
2334 | series.get_number_of_seasons(), 'episode': |
2335 | series.get_number_of_episodes()} |
2336 | |
2337 | self.series_info.set_text(info) |
2338 | self.series_title.set_text(series.get_title()) |
2339 | + self.list_indicator.show() |
2340 | + self.list_indicator.set_current(self.menu.selected_index + 1) |
2341 | else: |
2342 | self.series_info.set_text("") |
2343 | self.series_title.set_text("") |
2344 | - |
2345 | - def _update_menu(self, direction): |
2346 | - '''Update the menu based on the provided menu direction.''' |
2347 | - self.menu.move(direction) |
2348 | - self._update_series_info() |
2349 | - self.list_indicator.set_current(self.menu.get_current_position() + 1) |
2350 | - return False |
2351 | + self.list_indicator.hide() |
2352 | |
2353 | def _handle_up(self): |
2354 | '''Handle the up user event.''' |
2355 | - if self.menu.get_current_position() <= 6: |
2356 | + if self.menu.on_top: |
2357 | return True # Move control back to tab bar |
2358 | else: |
2359 | - return self._update_menu(self.menu.UP) |
2360 | + self.menu.up() |
2361 | + return False |
2362 | |
2363 | def _handle_down(self): |
2364 | '''Handle the down user event.''' |
2365 | - return self._update_menu(self.menu.DOWN) |
2366 | + self.menu.down() |
2367 | + return False |
2368 | |
2369 | def _handle_left(self): |
2370 | '''Handle the left user event.''' |
2371 | - return self._update_menu(self.menu.LEFT) |
2372 | + self.menu.left() |
2373 | + return False |
2374 | |
2375 | def _handle_right(self): |
2376 | '''Handle the right user event.''' |
2377 | - return self._update_menu(self.menu.RIGHT) |
2378 | + self.menu.right() |
2379 | + return False |
2380 | |
2381 | - def _handle_select(self): |
2382 | + def _handle_select(self, event=None): |
2383 | '''Handle the select user event.''' |
2384 | - series = self.menu.get_current_menuitem().get_userdata() |
2385 | + series = self.menu.selected_userdata |
2386 | kwargs = { 'tv_series' : series } |
2387 | self.callback("tv_series", kwargs) |
2388 | return False |
2389 | |
2390 | def _on_activated(self, event=None): |
2391 | '''Tab activated.''' |
2392 | - self._update_series_info() |
2393 | - self.menu.set_active(True) |
2394 | - self.menu.set_opacity(255) |
2395 | + if self.tab_group is not None: |
2396 | + self.tab_group.active = False |
2397 | + self.menu.active = True |
2398 | + self.active = True |
2399 | + self._update_serie_info() |
2400 | return False |
2401 | |
2402 | def _on_deactivated(self, event=None): |
2403 | '''Tab deactivated.''' |
2404 | - self._update_series_info() |
2405 | - self.menu.set_active(False) |
2406 | - self.menu.set_opacity(128) |
2407 | + self.active = False |
2408 | + self.menu.active = False |
2409 | + self._update_serie_info() |
2410 | return False |
2411 | |
2412 | + def _on_menu_filled(self, event=None): |
2413 | + '''Handles filled event.''' |
2414 | + self.throbber.hide() |
2415 | + |
2416 | |
2417 | === modified file 'entertainerlib/frontend/gui/tabs/tab.py' |
2418 | --- entertainerlib/frontend/gui/tabs/tab.py 2009-05-12 17:18:10 +0000 |
2419 | +++ entertainerlib/frontend/gui/tabs/tab.py 2009-05-21 16:12:09 +0000 |
2420 | @@ -40,6 +40,7 @@ |
2421 | |
2422 | # show/hide animation on the Tab |
2423 | self.timeline = clutter.Timeline(30, 60) |
2424 | + self.timeline.connect('completed', self._on_timeline_completed) |
2425 | self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func) |
2426 | self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha) |
2427 | self.behaviour.apply(self) |
2428 | @@ -48,6 +49,7 @@ |
2429 | self._active = None |
2430 | self._visible = False |
2431 | self.set_opacity(0) |
2432 | + self.hide() |
2433 | |
2434 | def _get_active(self): |
2435 | '''active property getter.''' |
2436 | @@ -81,6 +83,7 @@ |
2437 | self._visible = boolean |
2438 | |
2439 | if boolean: |
2440 | + self.show() |
2441 | self.timeline.set_direction(clutter.TIMELINE_FORWARD) |
2442 | self.timeline.rewind() |
2443 | self.timeline.start() |
2444 | @@ -90,6 +93,11 @@ |
2445 | |
2446 | visible = property(_get_visible, _set_visible) |
2447 | |
2448 | + def _on_timeline_completed(self, timeline): |
2449 | + """Hides the Tab on the end of the fade out animation.""" |
2450 | + if timeline.get_direction() == clutter.TIMELINE_BACKWARD: |
2451 | + self.hide() |
2452 | + |
2453 | def can_activate(self): |
2454 | ''' |
2455 | Returns a boolean whether or not this tab can be activated. If tab |
2456 | @@ -135,7 +143,7 @@ |
2457 | '''Dummy method for unimplemented handlers in subclass.''' |
2458 | return False |
2459 | |
2460 | - def _handle_select(self): |
2461 | + def _handle_select(self, event=None): |
2462 | '''Dummy method for unimplemented handlers in subclass.''' |
2463 | return False |
2464 | |
2465 | |
2466 | === modified file 'entertainerlib/frontend/gui/tabs/tracks_tab.py' |
2467 | --- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-05-14 03:22:31 +0000 |
2468 | +++ entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-07-16 20:31:22 +0000 |
2469 | @@ -1,14 +1,14 @@ |
2470 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
2471 | '''Tracks tab for the artist screen''' |
2472 | |
2473 | -import clutter |
2474 | import pango |
2475 | |
2476 | from entertainerlib.frontend.gui.tabs.tab import Tab |
2477 | from entertainerlib.frontend.gui.widgets.label import Label |
2478 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
2479 | +from entertainerlib.frontend.gui.widgets.loading_animation import ( |
2480 | + LoadingAnimation) |
2481 | from entertainerlib.frontend.gui.widgets.text_menu import TextMenu |
2482 | -from entertainerlib.frontend.gui.widgets.text_menu_item import TextMenuItem |
2483 | |
2484 | class TracksTab(Tab): |
2485 | '''Tab for the artist screen to show track listings''' |
2486 | @@ -17,31 +17,22 @@ |
2487 | tab_title=_("Tracks")): |
2488 | Tab.__init__(self, name, tab_title, move_to_new_screen_callback) |
2489 | |
2490 | - self.menu = TextMenu(self.theme, self.config.show_effects()) |
2491 | - self.menu.set_item_size(self.get_abs_x(0.2928), self.get_abs_y(0.0781)) |
2492 | - |
2493 | - cursor = clutter.Rectangle() |
2494 | - cursor.set_color((0, 0, 0, 0)) |
2495 | - self.menu.set_cursor(cursor) |
2496 | - |
2497 | - for track in tracks: |
2498 | - item = TextMenuItem(0.2928, 0.104, track.get_title()) |
2499 | - item.justified = True |
2500 | - item.set_userdata(track) |
2501 | - self.menu.add_actor(item) |
2502 | - |
2503 | - self.menu.set_orientation(TextMenu.HORIZONTAL) |
2504 | - self.menu.set_row_count(7) |
2505 | - self.menu.set_visible_column_count(3) |
2506 | - self.menu.set_position(self.get_abs_x(0.0586), self.get_abs_y(0.2083)) |
2507 | - self.menu.set_active(False) |
2508 | - self.menu.show() |
2509 | - |
2510 | - if self.config.show_effects(): |
2511 | - self.menu.set_animate(True) |
2512 | - |
2513 | + # Start the loading animation while the menu is loading |
2514 | + self.throbber = LoadingAnimation(0.1, 0.1) |
2515 | + self.throbber.show() |
2516 | + self.add(self.throbber) |
2517 | + |
2518 | + self.menu = TextMenu(0.0586, 0.2083, 0.2928, 0.0781) |
2519 | + self.menu.items_per_row = 3 |
2520 | + self.menu.visible_rows = 7 |
2521 | + self.menu.visible_cols = 3 |
2522 | + self.menu.active = False |
2523 | + self.menu.cursor = None |
2524 | self.add(self.menu) |
2525 | |
2526 | + tracks_list = [[track.get_title(), None, track] for track in tracks] |
2527 | + self.menu.async_add_artists(tracks_list) |
2528 | + |
2529 | self.track_title = Label(0.045, "title", 0.22, 0.79, "") |
2530 | self.track_title.set_ellipsize(pango.ELLIPSIZE_END) |
2531 | self.track_title.width = 0.366 |
2532 | @@ -56,78 +47,88 @@ |
2533 | self.add(self.track_length) |
2534 | |
2535 | self.li = ListIndicator(0.77, 0.8, 0.18, 0.045, |
2536 | - ListIndicator.HORIZONTAL) |
2537 | - self.li.set_maximum(self.menu.get_number_of_items()) |
2538 | + ListIndicator.VERTICAL) |
2539 | + self.li.set_maximum(len(tracks)) |
2540 | self.li.show() |
2541 | self.add(self.li) |
2542 | |
2543 | self.connect('activated', self._on_activated) |
2544 | self.connect('deactivated', self._on_deactivated) |
2545 | + self.menu.connect("moved", self._update_track_info) |
2546 | + self.menu.connect("selected", self._handle_select) |
2547 | + self.menu.connect("activated", self._on_activated) |
2548 | + self.menu.connect("filled", self._on_menu_filled) |
2549 | |
2550 | def can_activate(self): |
2551 | '''Tracks tab will always be created from an existing artist with at |
2552 | least one track.''' |
2553 | return True |
2554 | |
2555 | - def _update_track_info(self): |
2556 | + def _update_track_info(self, event=None): |
2557 | '''Update the track information labels''' |
2558 | if self.active: |
2559 | - track = self.menu.get_current_menuitem().get_userdata() |
2560 | + track = self.menu.selected_userdata |
2561 | self.track_title.set_text(track.get_title()) |
2562 | self.track_length.set_text(str(track.get_length() / 60) + ":" + \ |
2563 | str(track.get_length() % 60).zfill(2)) |
2564 | self.track_number.set_text(_("Track %(track)s from %(album)s") % \ |
2565 | {'track': track.get_tracknumber(), |
2566 | 'album': track.get_album().get_title()}) |
2567 | + self.li.show() |
2568 | + self.li.set_current(self.menu.selected_index + 1) |
2569 | else: |
2570 | self.track_title.set_text("") |
2571 | self.track_length.set_text("") |
2572 | self.track_number.set_text("") |
2573 | - |
2574 | - def _update_menu(self, direction): |
2575 | - '''Update the menu based on the provided menu direction.''' |
2576 | - self.menu.move(direction) |
2577 | - self._update_track_info() |
2578 | - self.li.set_current(self.menu.get_current_position() + 1) |
2579 | - return False |
2580 | + self.li.hide() |
2581 | |
2582 | def _handle_up(self): |
2583 | '''Handle the up user event.''' |
2584 | - if self.menu.get_cursor_position()[1] == 0: |
2585 | + if self.menu.on_top: |
2586 | return True # Move control back to tab bar |
2587 | else: |
2588 | - return self._update_menu(self.menu.UP) |
2589 | + self.menu.up() |
2590 | + return False |
2591 | |
2592 | def _handle_down(self): |
2593 | '''Handle the down user event.''' |
2594 | - return self._update_menu(self.menu.DOWN) |
2595 | + self.menu.down() |
2596 | + return False |
2597 | |
2598 | def _handle_left(self): |
2599 | '''Handle the left user event.''' |
2600 | - return self._update_menu(self.menu.LEFT) |
2601 | + self.menu.left() |
2602 | + return False |
2603 | |
2604 | def _handle_right(self): |
2605 | '''Handle the right user event.''' |
2606 | - return self._update_menu(self.menu.RIGHT) |
2607 | + self.menu.right() |
2608 | + return False |
2609 | |
2610 | - def _handle_select(self): |
2611 | + def _handle_select(self, event=None): |
2612 | '''Handle the select user event.''' |
2613 | - track = self.menu.get_current_menuitem().get_userdata() |
2614 | + track = self.menu.selected_userdata |
2615 | kwargs = { 'track' : track } |
2616 | self.callback("audio_play", kwargs) |
2617 | return False |
2618 | |
2619 | def _on_activated(self, event=None): |
2620 | '''Tab activated.''' |
2621 | + if self.tab_group is not None: |
2622 | + self.tab_group.active = False |
2623 | + self.menu.active = True |
2624 | + self.active = True |
2625 | self._update_track_info() |
2626 | - self.menu.set_active(True) |
2627 | - self.menu.set_opacity(255) |
2628 | return False |
2629 | |
2630 | def _on_deactivated(self, event=None): |
2631 | '''Tab deactivated.''' |
2632 | + self.active = False |
2633 | + self.menu.active = False |
2634 | self._update_track_info() |
2635 | - self.menu.set_active(False) |
2636 | - self.menu.set_opacity(128) |
2637 | return False |
2638 | |
2639 | + def _on_menu_filled(self, event=None): |
2640 | + '''Handles filled event.''' |
2641 | + self.throbber.hide() |
2642 | + |
2643 | |
2644 | === modified file 'entertainerlib/frontend/gui/tabs/video_clips_tab.py' |
2645 | --- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-05-14 03:22:31 +0000 |
2646 | +++ entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-07-14 11:06:21 +0000 |
2647 | @@ -3,15 +3,14 @@ |
2648 | |
2649 | import os |
2650 | |
2651 | -import gtk |
2652 | import pango |
2653 | |
2654 | from entertainerlib.frontend.gui.tabs.tab import Tab |
2655 | from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu |
2656 | -from entertainerlib.frontend.gui.widgets.image_menu_item import ImageMenuItem |
2657 | from entertainerlib.frontend.gui.widgets.label import Label |
2658 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
2659 | -from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture |
2660 | +from entertainerlib.frontend.gui.widgets.loading_animation import ( |
2661 | + LoadingAnimation) |
2662 | |
2663 | class VideoClipsTab(Tab): |
2664 | """ |
2665 | @@ -35,7 +34,17 @@ |
2666 | if self.video_library.get_number_of_video_clips() == 0: |
2667 | self._create_empty_library_notice() |
2668 | else: |
2669 | - self._create_menu() |
2670 | + # Start the loading animation while the menu is loading |
2671 | + self.throbber = LoadingAnimation(0.7, 0.1) |
2672 | + self.throbber.show() |
2673 | + self.add(self.throbber) |
2674 | + |
2675 | + self.menu = self._create_menu() |
2676 | + self.add(self.menu) |
2677 | + self.menu.connect("moved", self._update_clip_info) |
2678 | + self.menu.connect("selected", self._handle_select) |
2679 | + self.menu.connect("activated", self._on_activated) |
2680 | + self.menu.connect("filled", self._on_menu_filled) |
2681 | |
2682 | self.connect('activated', self._on_activated) |
2683 | self.connect('deactivated', self._on_deactivated) |
2684 | @@ -65,33 +74,19 @@ |
2685 | Create a view that is displayed when there are indexed clips in |
2686 | the video library. |
2687 | """ |
2688 | - self.menu = ImageMenu(self.theme) |
2689 | - self.menu.set_item_size(self.get_abs_x(0.233), self.get_abs_y(0.25)) |
2690 | - self.menu.set_item_spacing(self.get_abs_y(0.025)) |
2691 | - self.menu.set_visible_column_count(2) |
2692 | - self.menu.set_row_count(3) |
2693 | - self.menu.set_orientation(self.menu.VERTICAL) |
2694 | + menu = ImageMenu(0.04, 0.16, 0.23, self.y_for_x(0.23) * 0.7) |
2695 | + menu.items_per_col = 2 |
2696 | + menu.visible_rows = 2 |
2697 | + menu.visible_cols = 4 |
2698 | |
2699 | clips = self.video_library.get_video_clips() |
2700 | - for clip in clips: |
2701 | - pix_buffer = gtk.gdk.pixbuf_new_from_file(clip.get_thumbnail_url()) |
2702 | - texture = RoundedTexture(0.0, 0.0, 0.233, 0.25, pix_buffer) |
2703 | - item = ImageMenuItem(0.233, 0.5625, texture) |
2704 | - item.set_userdata(clip) |
2705 | - self.menu.add_actor(item) |
2706 | - |
2707 | - self.menu.set_position(self.get_abs_x(0.15), self.get_abs_y(0.2)) |
2708 | - self.menu.set_active(False) |
2709 | - |
2710 | - if self.config.show_effects(): |
2711 | - self.menu.set_animate(True) |
2712 | - |
2713 | - self.add(self.menu) |
2714 | + clips_list = [[clip.get_thumbnail_url(), clip] for clip in clips] |
2715 | + menu.async_add_clips(clips_list) |
2716 | |
2717 | # Create list indicator |
2718 | self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045, |
2719 | - ListIndicator.VERTICAL) |
2720 | - self.list_indicator.set_maximum(self.menu.get_number_of_items()) |
2721 | + ListIndicator.HORIZONTAL) |
2722 | + self.list_indicator.set_maximum(len(clips)) |
2723 | self.list_indicator.show() |
2724 | self.add(self.list_indicator) |
2725 | |
2726 | @@ -107,46 +102,48 @@ |
2727 | self.clip_info.width = 0.5 |
2728 | self.add(self.clip_info) |
2729 | |
2730 | - def _update_clip_info(self): |
2731 | + return menu |
2732 | + |
2733 | + def _update_clip_info(self, event=None): |
2734 | '''Update the VideoClip information labels.''' |
2735 | if self.active: |
2736 | - clip = self.menu.get_current_menuitem().get_userdata() |
2737 | + clip = self.menu.selected_userdata |
2738 | (folder, filename) = os.path.split(clip.get_filename()) |
2739 | self.clip_title.set_text(filename) |
2740 | self.clip_info.set_text(folder) |
2741 | + self.list_indicator.show() |
2742 | + self.list_indicator.set_current(self.menu.selected_index + 1) |
2743 | else: |
2744 | self.clip_title.set_text("") |
2745 | self.clip_info.set_text("") |
2746 | - |
2747 | - def _update_menu(self, direction): |
2748 | - '''Update the menu based on the provided menu direction.''' |
2749 | - self.menu.move(direction) |
2750 | - self._update_clip_info() |
2751 | - self.list_indicator.set_current(self.menu.get_current_position() + 1) |
2752 | - return False |
2753 | + self.list_indicator.hide() |
2754 | |
2755 | def _handle_up(self): |
2756 | '''Handle the up user event.''' |
2757 | - if self.menu.get_current_position() <= 2: |
2758 | + if self.menu.on_top: |
2759 | return True # Move control back to tab bar |
2760 | else: |
2761 | - return self._update_menu(self.menu.UP) |
2762 | + self.menu.up() |
2763 | + return False |
2764 | |
2765 | def _handle_down(self): |
2766 | '''Handle the down user event.''' |
2767 | - return self._update_menu(self.menu.DOWN) |
2768 | + self.menu.down() |
2769 | + return False |
2770 | |
2771 | def _handle_left(self): |
2772 | '''Handle the left user event.''' |
2773 | - return self._update_menu(self.menu.LEFT) |
2774 | + self.menu.left() |
2775 | + return False |
2776 | |
2777 | def _handle_right(self): |
2778 | '''Handle the right user event.''' |
2779 | - return self._update_menu(self.menu.RIGHT) |
2780 | + self.menu.right() |
2781 | + return False |
2782 | |
2783 | - def _handle_select(self): |
2784 | + def _handle_select(self, event=None): |
2785 | '''Handle the select user event.''' |
2786 | - clip = self.menu.get_current_menuitem().get_userdata() |
2787 | + clip = self.menu.selected_userdata |
2788 | self.media_player.set_media(clip) |
2789 | self.media_player.play() |
2790 | self.callback("video_osd") |
2791 | @@ -154,15 +151,21 @@ |
2792 | |
2793 | def _on_activated(self, event=None): |
2794 | '''Tab activated.''' |
2795 | + if self.tab_group is not None: |
2796 | + self.tab_group.active = False |
2797 | + self.menu.active = True |
2798 | + self.active = True |
2799 | self._update_clip_info() |
2800 | - self.menu.set_active(True) |
2801 | - self.menu.set_opacity(255) |
2802 | return False |
2803 | |
2804 | def _on_deactivated(self, event=None): |
2805 | '''Tab deactivated.''' |
2806 | + self.active = False |
2807 | + self.menu.active = False |
2808 | self._update_clip_info() |
2809 | - self.menu.set_active(False) |
2810 | - self.menu.set_opacity(128) |
2811 | return False |
2812 | |
2813 | + def _on_menu_filled(self, event=None): |
2814 | + '''Handles filled event.''' |
2815 | + self.throbber.hide() |
2816 | + |
2817 | |
2818 | === modified file 'entertainerlib/frontend/gui/widgets/base.py' |
2819 | --- entertainerlib/frontend/gui/widgets/base.py 2009-04-28 22:30:06 +0000 |
2820 | +++ entertainerlib/frontend/gui/widgets/base.py 2009-07-15 21:22:17 +0000 |
2821 | @@ -21,3 +21,12 @@ |
2822 | result = int(self.config.get_stage_height() * percentage) |
2823 | return result |
2824 | |
2825 | + def y_for_x(self, percentage): |
2826 | + """ |
2827 | + Returns the percentage of stage height giving the same number of pixels |
2828 | + equivalent to `percentage` applied to the stage width. |
2829 | + """ |
2830 | + stage_ratio = float(self.config.get_stage_width()) |
2831 | + stage_ratio /= self.config.get_stage_height() |
2832 | + return stage_ratio * percentage |
2833 | + |
2834 | |
2835 | === modified file 'entertainerlib/frontend/gui/widgets/grid_menu.py' |
2836 | --- entertainerlib/frontend/gui/widgets/grid_menu.py 2009-05-10 06:24:43 +0000 |
2837 | +++ entertainerlib/frontend/gui/widgets/grid_menu.py 2009-07-19 19:27:31 +0000 |
2838 | @@ -1,672 +1,591 @@ |
2839 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
2840 | -'''GridMenu contains a grid of textures and allows user to select one''' |
2841 | +'''GridMenu contains a grid of MenuItem based widgets.''' |
2842 | + |
2843 | +import math |
2844 | |
2845 | import clutter |
2846 | - |
2847 | -class GridMenu(clutter.Group): |
2848 | - """ |
2849 | - GridMenu widget |
2850 | - |
2851 | - This is a flexible menu widget that supports horizontal, vertical, |
2852 | - scrollable, multirow menus. Real menus should inherit this class |
2853 | - and override few methods. |
2854 | - """ |
2855 | - |
2856 | - # Orientation constants |
2857 | - HORIZONTAL = 0 |
2858 | - VERTICAL = 1 |
2859 | - |
2860 | - # Direction constants |
2861 | - UP = 2 |
2862 | - DOWN = 3 |
2863 | - LEFT = 4 |
2864 | - RIGHT = 5 |
2865 | - |
2866 | - # Cursor placement (Is cursor drawn above or below of all menuitems) |
2867 | - BELOW = 6 |
2868 | - ABOVE = 7 |
2869 | - |
2870 | - def __init__(self, orientation = 1): # Default orientation is VERTICAL |
2871 | - """ |
2872 | - Initialize GridMenu widget |
2873 | - @param orientation: Orientation of the menu. Use class constatants. |
2874 | - """ |
2875 | +import gobject |
2876 | + |
2877 | +from entertainerlib.frontend.gui.widgets.base import Base |
2878 | +from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer |
2879 | + |
2880 | +class GridMenu(Base, clutter.Group): |
2881 | + """ |
2882 | + GridMenu widget. |
2883 | + |
2884 | + A core widget to handle MenuItem in a grid with a cursor. |
2885 | + This widget provides all the necessary logic to move items and the cursor. |
2886 | + """ |
2887 | + __gsignals__ = { |
2888 | + 'activated' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
2889 | + 'moved' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
2890 | + 'selected' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
2891 | + } |
2892 | + |
2893 | + MODE_NONE = 0 |
2894 | + MODE_SELECT = 1 |
2895 | + MODE_SEEK = 2 |
2896 | + |
2897 | + def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1): |
2898 | + Base.__init__(self) |
2899 | clutter.Group.__init__(self) |
2900 | |
2901 | - self.itemgroup = clutter.Group() # Group for menuitems |
2902 | - self.items = [] # List of Menuitems |
2903 | - self.orientation = orientation # Menu orientation |
2904 | - self.row_count = 1 # Items per row |
2905 | - self.visible_cols = 8 # Number of visible columns |
2906 | - self.cell_height = 100 # Menuitem height |
2907 | - self.cell_width = 100 # Menuitem width |
2908 | - self.cell_spacing = 0 # Space between items |
2909 | - self.logical_position = 0 # Logical position (in item list) |
2910 | - self.cursor_position = (0, 0) # Cursor position (on screen) |
2911 | - self.content_position = (0, 0) # Content position |
2912 | - self.animate = False # Should we animate this widget |
2913 | - self.cursor = None # Cursor (clutter.Actor) |
2914 | - self.cursor_placement = 7 # Cursor placement (Default: Above) |
2915 | - self.active = True # Is this menu active |
2916 | - self.content_timeline = None # Content scroll timeline |
2917 | - self.cursor_timeline = None # Cursor move timeline |
2918 | - |
2919 | - self.add(self.itemgroup) |
2920 | - |
2921 | - # Default red transparent cursor |
2922 | - c = clutter.Rectangle() |
2923 | - c.set_color((255, 0, 0, 128)) |
2924 | - self.set_cursor(c) |
2925 | - |
2926 | - def set_cursor(self, cursor): |
2927 | - """ |
2928 | - Set cursor actor. This is usually a Texture or clutter.Rectangle. |
2929 | - @param cursor: clutter.Actor - Cursor graphics |
2930 | - """ |
2931 | - # Remove old if exists |
2932 | - if self.cursor is not None: |
2933 | - self.remove(self.cursor) |
2934 | - |
2935 | - # Set new cursor |
2936 | - self.cursor = cursor |
2937 | - |
2938 | - # If widget is currently active display cursor |
2939 | - if self.active: |
2940 | - self.cursor.show() |
2941 | - |
2942 | - self.add(self.cursor) |
2943 | - self.cursor.set_size(self.cell_width, self.cell_height) |
2944 | - self._scale_cursor() |
2945 | - |
2946 | - # Set cursor placement |
2947 | - if self.cursor_placement == self.BELOW: |
2948 | - self.cursor.lower_bottom() |
2949 | - elif self.cursor_placement == self.ABOVE: |
2950 | - self.cursor.raise_top() |
2951 | - |
2952 | - def set_cursor_placement(self, placement): |
2953 | - """ |
2954 | - Set cursor placement. Is cursor drawn below or above of all menuitems. |
2955 | - @param placement: GridMenu.BELOW or GridMenu.ABOVE |
2956 | - """ |
2957 | - self.cursor_placement = placement |
2958 | - if self.cursor_placement == self.BELOW: |
2959 | - self.cursor.lower_bottom() |
2960 | - elif self.cursor_placement == self.ABOVE: |
2961 | - self.cursor.raise_top() |
2962 | - |
2963 | - def set_active(self, boolean): |
2964 | - """ |
2965 | - Set menu widget to active / disabled. Please see API references of |
2966 | - the self._animate_items_on_state_change() method. It handels all actor |
2967 | - changes when menu widget state changes. |
2968 | - """ |
2969 | - self.active = boolean |
2970 | - self._animate_items_on_state_change() |
2971 | - |
2972 | - def is_active(self): |
2973 | - """ |
2974 | - Is this menu active? |
2975 | - @return: True if menu is active, otherwise False |
2976 | - """ |
2977 | - return self.active |
2978 | - |
2979 | - def set_animate(self, boolean): |
2980 | - """ |
2981 | - Enable/disable widget animation. |
2982 | - @param boolean: True if widget should be animated, otherwise False |
2983 | - """ |
2984 | - self.animate = boolean |
2985 | - |
2986 | - def add_actor(self, item): |
2987 | - """ |
2988 | - Add menuitem to this menu. |
2989 | - @param item: clutter.Actor object |
2990 | - """ |
2991 | - #item.set_size(self.cell_width, self.cell_height) |
2992 | - self._scale_menuitem(item) # Scale item |
2993 | - self.items.append(item) # Add to logical list |
2994 | - self.itemgroup.add(item) # Add to menuitem group (visible list) |
2995 | - self._update_menuitem_position(item, len(self.items) - 1) |
2996 | - |
2997 | - # Update cursor |
2998 | - if self.cursor_placement == self.BELOW: |
2999 | - self.cursor.lower_bottom() |
3000 | - elif self.cursor_placement == self.ABOVE: |
3001 | - self.cursor.raise_top() |
3002 | - |
3003 | - # If this is a first item in the menu. |
3004 | - if len(self.items) == 1: |
3005 | - self._update_selected_item(0) |
3006 | - self.set_visible_column_count(self.visible_cols) |
3007 | - |
3008 | - def remove_actor(self, item): |
3009 | - """ |
3010 | - Remove menuitem from this menu. |
3011 | - @param item: clutter.Actor object |
3012 | - """ |
3013 | - try: |
3014 | - self.items.remove(item) # Remove from logical list |
3015 | - self.itemgroup.remove(item) # Remove from visible list group |
3016 | - for i in range(len(self.items)): |
3017 | - self._update_menuitem_position(self.items[i], i) |
3018 | - except: |
3019 | - raise Exception("Couldn't remove menuitem. Item doesn't exist.") |
3020 | - |
3021 | - def remove_all(self): |
3022 | - """ |
3023 | - Remove all menuitems from this menu. |
3024 | - """ |
3025 | + self.motion_duration = 100 # Default duration of animations ms. |
3026 | + self.cursor_below = True # Is the cursor below items? |
3027 | + self._active = None |
3028 | + self._is_vertical = True |
3029 | self.items = [] |
3030 | - self.itemgroup.remove_all() |
3031 | - |
3032 | - def select(self, logical_position): |
3033 | - '''Selects the item at the passed logical_position.''' |
3034 | - # pylint: disable-msg=W0612 |
3035 | - target = ( int(logical_position / self.row_count), |
3036 | - int(logical_position % self.row_count)) |
3037 | - |
3038 | - move_y = self.cursor_position[0] - target[0] |
3039 | - move_x = self.cursor_position[1] - target[1] |
3040 | - |
3041 | - #Move Vertically |
3042 | - if move_y > 0: |
3043 | - for i in xrange(move_y): |
3044 | - self._move_cursor(self.UP) |
3045 | - else: |
3046 | - for i in xrange(-move_y): |
3047 | - self._move_cursor(self.DOWN) |
3048 | - |
3049 | - #Move Horizontally |
3050 | - if move_x > 0: |
3051 | - for i in xrange(move_x): |
3052 | - self._move_cursor(self.RIGHT) |
3053 | - else: |
3054 | - for i in xrange(-move_x): |
3055 | - self._move_cursor(self.LEFT) |
3056 | - |
3057 | - def get_n_children(self): |
3058 | - """ |
3059 | - Override clutter.Group method. Returns number of menuitems. |
3060 | - """ |
3061 | - return len(self.items) |
3062 | - |
3063 | - def get_nth_child(self, index): |
3064 | - """ |
3065 | - Override clutter.Group method. Returns one menuitem. |
3066 | - """ |
3067 | - return self.items[index] |
3068 | - |
3069 | - def get_number_of_items(self): |
3070 | - """ |
3071 | - Get number of menuitems. |
3072 | - @return: Integer |
3073 | - """ |
3074 | - return len(self.items) |
3075 | - |
3076 | - def get_current_position(self): |
3077 | - """ |
3078 | - Get logical position of the menu cursor. |
3079 | - """ |
3080 | - return self.logical_position |
3081 | - |
3082 | - def get_current_menuitem(self): |
3083 | - """ |
3084 | - Get current menuitem. (Menuitem under menu cursor) |
3085 | - @return: clutter.Actor |
3086 | - """ |
3087 | - return self.items[self.logical_position] |
3088 | - |
3089 | - def get_cursor_position(self): |
3090 | - """ |
3091 | - Get cursor position. This is a (x,y) coordinate of the cursor. Cursor |
3092 | - position is always in displayble area. |
3093 | - @return: (x,y) tuple |
3094 | - """ |
3095 | - return self.cursor_position |
3096 | - |
3097 | - def set_orientation(self, orientation): |
3098 | - """ |
3099 | - Set widget orientation. This defines which way widget is scrolled when |
3100 | - all items don't fit into the widget area at the same time. |
3101 | - @param orientation: GridMenu.HORIZONTAL or GridMenu.VERTICAL |
3102 | - """ |
3103 | - if orientation < 0 or orientation > 1: |
3104 | - raise Exception("Invalid orientation") |
3105 | - self.orientation = orientation |
3106 | - for i in range(len(self.items)): |
3107 | - self._update_menuitem_position(self.items[i], i) |
3108 | - |
3109 | - def get_orientation(self): |
3110 | - """ |
3111 | - Get menu orientation. |
3112 | - @return: GridMenu.VERTICAL or GridMenu.HORIZONTAL (Integer constant) |
3113 | - """ |
3114 | - return self.orientation |
3115 | - |
3116 | - def set_row_count(self, count): |
3117 | - """ |
3118 | - Set how many items there should be on one row. Notice that row's |
3119 | - direction depends on widget's orientation. For example, 'normal menu' |
3120 | - would be VERTICAL with a row count 1. |
3121 | - @param count: Number of items per row |
3122 | - """ |
3123 | - if count < 1: |
3124 | - raise Exception("Row count must be a positive value.") |
3125 | - self.row_count = count |
3126 | - for i in range(len(self.items)): |
3127 | - self._update_menuitem_position(self.items[i], i) |
3128 | - |
3129 | - def get_row_count(self): |
3130 | - """ |
3131 | - Get row count. |
3132 | - @return: Integer |
3133 | - """ |
3134 | - return self.row_count |
3135 | - |
3136 | - def set_visible_column_count(self, count): |
3137 | - """ |
3138 | - Set how many columns are allowed to be visible at the same time. Column |
3139 | - direction depends on widget orientation. For example, if menu is |
3140 | - VERTICAL, row_count is 1 and visible columns is 8, then there are at |
3141 | - most 8 visible items which are scrolled vertically. |
3142 | - """ |
3143 | - if count < 1: |
3144 | - raise Exception("Only positive values allowed!") |
3145 | - self.visible_cols = count |
3146 | - |
3147 | - if self.orientation == self.VERTICAL: |
3148 | - w = (self.cell_width + self.cell_spacing) * self.row_count |
3149 | - h = count * (self.cell_height + self.cell_spacing) |
3150 | - else: |
3151 | - w = count * (self.cell_width + self.cell_spacing) |
3152 | - h = (self.cell_height + self.cell_spacing) * self.row_count |
3153 | - |
3154 | - self.remove_clip() |
3155 | - #print "clip area: (width: " + str(w) + " and height: " + str(h) |
3156 | - self.set_clip(0, 0, w, h) |
3157 | - |
3158 | - def get_visible_column_count(self): |
3159 | - """ |
3160 | - Get the number of visible columns. |
3161 | - @return: Integer |
3162 | - """ |
3163 | - return self.visible_cols |
3164 | - |
3165 | - def set_item_spacing(self, space): |
3166 | - """ |
3167 | - Set how many pixels should be left between menu items. |
3168 | - @param space: Item spacing in pixels (0 or higher) |
3169 | - """ |
3170 | - if space < 0: |
3171 | - raise Exception("Spacing must be a positive integer.") |
3172 | - self.cell_spacing = space |
3173 | - for i in range(len(self.items)): |
3174 | - self._update_menuitem_position(self.items[i], i) |
3175 | - |
3176 | - def set_item_height(self, height): |
3177 | - """ |
3178 | - Set the height of the one cell in the grid. |
3179 | - @param height: Height in pixels (positive value) |
3180 | - """ |
3181 | - |
3182 | - if height < 1: |
3183 | - raise Exception("Height must be a positive value") |
3184 | - |
3185 | - self.cell_height = height |
3186 | - |
3187 | - # Update cursor |
3188 | - self._scale_cursor() |
3189 | - |
3190 | - # Update menuitems |
3191 | - for item in self.items: |
3192 | - self._scale_menuitem(item) |
3193 | - for i in range(len(self.items)): |
3194 | - self._update_menuitem_position(self.items[i], i) |
3195 | - |
3196 | - def set_item_width(self, width): |
3197 | - """ |
3198 | - Set the width of the one cell in the grid. |
3199 | - @param height: Width in pixels (positive value) |
3200 | - """ |
3201 | - if width < 1: |
3202 | - raise Exception("Width must be a positive value") |
3203 | - |
3204 | - self.cell_width = width |
3205 | - |
3206 | - # Update cursor |
3207 | - self._scale_cursor() |
3208 | - |
3209 | - # Update menuitems |
3210 | - for item in self.items: |
3211 | - self._scale_menuitem(item) |
3212 | - for i in range(len(self.items)): |
3213 | - self._update_menuitem_position(self.items[i], i) |
3214 | - |
3215 | - def set_item_size(self, width, height): |
3216 | - """ |
3217 | - Set the width of the one cell in the grid. |
3218 | - @param height: Width in pixels (positive value) |
3219 | - """ |
3220 | - if width < 1 or height < 1 : |
3221 | - raise Exception("Width and height must be positive values!") |
3222 | - |
3223 | - self.cell_width = width |
3224 | - self.cell_height = height |
3225 | - |
3226 | - # Update clipping |
3227 | - #self.set_visible_column_count(self.visible_cols) |
3228 | - |
3229 | - # Update cursor |
3230 | - self._scale_cursor() |
3231 | - |
3232 | - # Update menuitems |
3233 | - for item in self.items: |
3234 | - self._scale_menuitem(item) |
3235 | - for i in range(len(self.items)): |
3236 | - self._update_menuitem_position(self.items[i], i) |
3237 | - |
3238 | - def move(self, direction): |
3239 | - """ |
3240 | - Move to given direction. Cursor is moved or grid content is scrolled if |
3241 | - needed. |
3242 | - @param direction: GridMenu class variables (UP, DOWN, LEFT, RIGHT) |
3243 | - """ |
3244 | - if not self._is_move_legal(direction): |
3245 | - return |
3246 | - |
3247 | - # Current position of the content and cursor |
3248 | - cur_x, cur_y = self.cursor_position |
3249 | - con_x, con_y = self.content_position |
3250 | - |
3251 | - # Define limit value for cursor and content move |
3252 | - max_row = len(self.items) / self.row_count |
3253 | - if (len(self.items) % self.row_count) != 0: |
3254 | - max_row = max_row + 1 |
3255 | - |
3256 | - content_scroll_max = max_row - self.visible_cols |
3257 | - |
3258 | - if self.visible_cols > max_row: |
3259 | - # There are less rows than allowed to be visible at the same time |
3260 | - cursor_scroll_max = max_row - 1 |
3261 | - else: |
3262 | - cursor_scroll_max = self.visible_cols - 1 |
3263 | - |
3264 | - # Menu has a vertical orientation |
3265 | - if self.orientation == self.VERTICAL: |
3266 | - if direction == self.UP: |
3267 | - if cur_y == 0: |
3268 | - if con_y > 0: |
3269 | - self._move_content(1, self.DOWN) |
3270 | - else: |
3271 | - self._move_cursor(self.UP) |
3272 | - elif direction == self.DOWN: |
3273 | - if cur_y < cursor_scroll_max: |
3274 | - self._move_cursor(self.DOWN) |
3275 | - else: |
3276 | - if con_y < content_scroll_max: |
3277 | - self._move_content(1, self.UP) |
3278 | - elif direction == self.LEFT: |
3279 | - if cur_x > 0: |
3280 | - self._move_cursor(self.LEFT) |
3281 | - elif direction == self.RIGHT: |
3282 | - if cur_x < self.row_count - 1: |
3283 | - self._move_cursor(self.RIGHT) |
3284 | - |
3285 | - # Menu has a horizontal orientation |
3286 | - else: |
3287 | - if direction == self.UP: |
3288 | - if cur_y > 0: |
3289 | - self._move_cursor(self.UP) |
3290 | - elif direction == self.DOWN: |
3291 | - if cur_y < self.row_count - 1: |
3292 | - self._move_cursor(self.DOWN) |
3293 | - elif direction == self.LEFT: |
3294 | - if cur_x == 0: |
3295 | - if con_x > 0: |
3296 | - self._move_content(1, self.RIGHT) |
3297 | - else: |
3298 | - self._move_cursor(self.LEFT) |
3299 | - elif direction == self.RIGHT: |
3300 | - if cur_x < cursor_scroll_max: |
3301 | - self._move_cursor(self.RIGHT) |
3302 | - else: |
3303 | - if con_x < content_scroll_max: |
3304 | - self._move_content(1, self.LEFT) |
3305 | - |
3306 | - old_position = self.logical_position |
3307 | - |
3308 | - # Calculate logical position |
3309 | - if self.orientation == self.VERTICAL: |
3310 | - tmp = (self.cursor_position[1] + |
3311 | - self.content_position[1]) * self.row_count |
3312 | - self.logical_position = tmp + self.cursor_position[0] |
3313 | - else: |
3314 | - tmp = (self.cursor_position[0] + |
3315 | - self.content_position[0]) * self.row_count |
3316 | - self.logical_position = tmp + self.cursor_position[1] |
3317 | - |
3318 | - self._update_selected_item(self.logical_position, old_position) |
3319 | - |
3320 | - def _is_move_legal(self, direction): |
3321 | - """ |
3322 | - Check if move is legal. This prevents cursor from leaving menu items. |
3323 | - This could happen when grid's last row is not "full". |
3324 | - @param direction: GridMenu class variables (UP, DOWN, LEFT, RIGHT) |
3325 | - @return: True if move can be executed, otherwise False |
3326 | - """ |
3327 | - # If last row is "full" we can't go wrong |
3328 | - if len(self.items) % self.row_count == 0: |
3329 | - return True |
3330 | - |
3331 | - # -1 because rows start from 0 |
3332 | - last_full_row = len(self.items) / self.row_count -1 |
3333 | - items_on_last_row = len(self.items) % self.row_count |
3334 | - |
3335 | - # Checking for vertical menu |
3336 | - if self.orientation == self.VERTICAL: |
3337 | - current_row = self.cursor_position[1] + self.content_position[1] |
3338 | - if current_row == last_full_row and direction == self.DOWN: |
3339 | - if self.cursor_position[0] + 1 > items_on_last_row: |
3340 | - return False |
3341 | - if current_row == last_full_row + 1 and direction == self.RIGHT: |
3342 | - if self.logical_position == len(self.items) - 1: |
3343 | - return False |
3344 | - # Checking for horizontal menu |
3345 | - else: |
3346 | - current_row = self.cursor_position[0] + self.content_position[0] |
3347 | - if current_row == last_full_row and direction == self.RIGHT: |
3348 | - if self.cursor_position[1] + 1 > items_on_last_row: |
3349 | - return False |
3350 | - if current_row == last_full_row + 1 and direction == self.DOWN: |
3351 | - if self.logical_position == len(self.items) - 1: |
3352 | - return False |
3353 | - return True |
3354 | - |
3355 | - def _move_cursor(self, direction): |
3356 | - """ |
3357 | - Move cursor on grid. |
3358 | - """ |
3359 | - # Change logical position |
3360 | - x, y = self.cursor_position |
3361 | - if direction == self.UP: |
3362 | - self.cursor_position = (x, y-1) |
3363 | - elif direction == self.DOWN: |
3364 | - self.cursor_position = (x, y+1) |
3365 | - elif direction == self.LEFT: |
3366 | - self.cursor_position = (x-1, y) |
3367 | - elif direction == self.RIGHT: |
3368 | - self.cursor_position = (x+1, y) |
3369 | - |
3370 | - # Change cursor texture position |
3371 | - x_offset = self.cell_width + self.cell_spacing |
3372 | - y_offset = self.cell_height + self.cell_spacing |
3373 | - |
3374 | - # BELOW IS ANIMATION CODE FOR MENU CURSOR. SHOULD CURSOR BE ANIMATED? |
3375 | -# if self.animate: |
3376 | -# # Finish previous animation before new |
3377 | -# if self.cursor_timeline is not None and ( |
3378 | -# self.cursor_timeline.is_playing(): |
3379 | -# self.cursor_timeline.pause() |
3380 | -# self.cursor_timeline.advance(4) |
3381 | -# |
3382 | -# self.cursor_timeline = clutter.Timeline(4, 26) |
3383 | -# alpha = clutter.Alpha(self.cursor_timeline, clutter.ramp_inc_func) |
3384 | -# |
3385 | -# x = self.cursor.get_x() |
3386 | -# y = self.cursor.get_y() |
3387 | -# if direction == self.UP: |
3388 | -# behaviour = clutter.BehaviourPath(alpha, |
3389 | -# ((x,y), |
3390 | -# (x,y - y_offset))) |
3391 | -# elif direction == self.DOWN: |
3392 | -# behaviour = clutter.BehaviourPath(alpha, |
3393 | -# ((x,y), |
3394 | -# (x,y + y_offset))) |
3395 | -# elif direction == self.RIGHT: |
3396 | -# behaviour = clutter.BehaviourPath(alpha, |
3397 | -# ((x,y), |
3398 | -# (x + x_offset,y))) |
3399 | -# elif direction == self.LEFT: |
3400 | -# behaviour = clutter.BehaviourPath(alpha, |
3401 | -# ((x,y), |
3402 | -# (x - x_offset,y))) |
3403 | -# behaviour.apply(self.cursor) |
3404 | -# self.cursor_timeline.start() |
3405 | -# else: |
3406 | - if direction == self.UP: |
3407 | - self.cursor.move_by(0, -y_offset) |
3408 | - elif direction == self.DOWN: |
3409 | - self.cursor.move_by(0, y_offset) |
3410 | - elif direction == self.LEFT: |
3411 | - self.cursor.move_by(-x_offset, 0) |
3412 | - elif direction == self.RIGHT: |
3413 | - self.cursor.move_by(x_offset, 0) |
3414 | - |
3415 | - def _move_content(self, count, direction): |
3416 | - """ |
3417 | - Move menuitems to given direction. |
3418 | - @param count: How much we scroll (number of menuitems) |
3419 | - @param direction: Which direction we scroll (See class constants) |
3420 | - """ |
3421 | - # Change logical position |
3422 | - x, y = self.content_position |
3423 | - if direction == self.UP: |
3424 | - self.content_position = (x, y+1) |
3425 | - elif direction == self.DOWN: |
3426 | - self.content_position = (x, y-1) |
3427 | - elif direction == self.LEFT: |
3428 | - self.content_position = (x+1, y) |
3429 | - elif direction == self.RIGHT: |
3430 | - self.content_position = (x-1, y) |
3431 | - |
3432 | - x_offset = count * (self.cell_width + self.cell_spacing) |
3433 | - y_offset = count * (self.cell_height + self.cell_spacing) |
3434 | -# if self.animate: |
3435 | -# x = self.itemgroup.get_x() |
3436 | -# y = self.itemgroup.get_y() |
3437 | -# |
3438 | -# # Finish previous animation before new |
3439 | -# if self.content_timeline is not None and ( |
3440 | -# self.content_timeline.is_playing(): |
3441 | -# self.content_timeline.pause() |
3442 | -# self.content_timeline.advance(4) |
3443 | -# |
3444 | -# self.content_timeline = clutter.Timeline(4, 26) |
3445 | -# alpha = clutter.Alpha(self.content_timeline, |
3446 | -# clutter.ramp_inc_func) |
3447 | -# if direction == self.UP: |
3448 | -# behaviour = clutter.BehaviourPath(alpha, |
3449 | -# ((x,y), |
3450 | -# (x,y - y_offset))) |
3451 | -# elif direction == self.DOWN: |
3452 | -# behaviour = clutter.BehaviourPath(alpha, |
3453 | -# ((x,y), |
3454 | -# (x,y + y_offset))) |
3455 | -# elif direction == self.RIGHT: |
3456 | -# behaviour = clutter.BehaviourPath(alpha, |
3457 | -# ((x,y), |
3458 | -# (x + x_offset,y))) |
3459 | -# elif direction == self.LEFT: |
3460 | -# behaviour = clutter.BehaviourPath(alpha, |
3461 | -# ((x,y), |
3462 | -# (x - x_offset,y))) |
3463 | -# behaviour.apply(self.itemgroup) |
3464 | -# self.content_timeline.start() |
3465 | -# else: |
3466 | - if direction == self.UP: |
3467 | - self.itemgroup.move_by(0, -y_offset) |
3468 | - elif direction == self.DOWN: |
3469 | - self.itemgroup.move_by(0, y_offset) |
3470 | - elif direction == self.RIGHT: |
3471 | - self.itemgroup.move_by(x_offset, 0) |
3472 | - elif direction == self.LEFT: |
3473 | - self.itemgroup.move_by(-x_offset, 0) |
3474 | - |
3475 | - def _scale_menuitem(self, menuitem): |
3476 | - """ |
3477 | - Scale menuitem to fit into one cell. Preserves actor's aspect ratio. |
3478 | - @param menuitem: clutter.Actor |
3479 | - """ |
3480 | - w, h = menuitem.get_size() |
3481 | - |
3482 | - if w == self.cell_width and h == self.cell_height: |
3483 | - return # Already correct size, no scale needed |
3484 | - |
3485 | - x_ratio = self.cell_width / float(w) |
3486 | - y_ratio = self.cell_height / float(h) |
3487 | - if x_ratio > y_ratio: |
3488 | - menuitem.set_scale(self.cell_height /float(h), |
3489 | - self.cell_height / float(h)) |
3490 | - else: |
3491 | - menuitem.set_scale(self.cell_width / float(w), |
3492 | - self.cell_width / float(w)) |
3493 | - |
3494 | - def _scale_cursor(self): |
3495 | - """ |
3496 | - Scale cursor. Override this method if you want to use cursor that is |
3497 | - smaller or larger than menuitem. This default implementation scales |
3498 | - cursor into the size of cell of the grid. Aspect ratio is not |
3499 | - preserved! |
3500 | - """ |
3501 | - x_ratio = self.cell_width / float(self.cursor.get_width()) |
3502 | - y_ratio = self.cell_height / float(self.cursor.get_height()) |
3503 | - self.cursor.set_scale(x_ratio, y_ratio) |
3504 | - |
3505 | - def _update_menuitem_position(self, menuitem, logical_position): |
3506 | - """ |
3507 | - Recalculate menuitem's position. |
3508 | - @param menuitem: Menuitem that is repositioned (clutter.Actor) |
3509 | - @param logical_position: Logical position of the menuitem |
3510 | - """ |
3511 | - if logical_position == 0: |
3512 | - x = 0 |
3513 | - y = 0 |
3514 | - else: |
3515 | - row = (logical_position / self.row_count) # Item's row |
3516 | - col = logical_position % self.row_count # Item's column |
3517 | - if self.orientation == self.VERTICAL: |
3518 | - x = col * (self.cell_width + self.cell_spacing) |
3519 | - y = row * (self.cell_height + self.cell_spacing) |
3520 | - elif self.orientation == self.HORIZONTAL: |
3521 | - y = col * (self.cell_height + self.cell_spacing) |
3522 | - x = row * (self.cell_width + self.cell_spacing) |
3523 | - menuitem.set_position(x, y) |
3524 | - |
3525 | - def _update_selected_item(self, new_location, old_location=None): |
3526 | - """ |
3527 | - This method allows to modify currently selected item. This can be |
3528 | - overriden in inherited widgets. |
3529 | - @param new_location: Logical location of current menuitem |
3530 | - @param old_location: Logical location of last menuitem |
3531 | - """ |
3532 | - pass |
3533 | - |
3534 | - def _animate_items_on_state_change(self): |
3535 | - """ |
3536 | - This method is called when widget is set active or inactive. The method |
3537 | - allows widget to react this action. Method should be overriden in |
3538 | - inheriting classes. Default implementation doesn't do anything. |
3539 | - """ |
3540 | - pass |
3541 | + |
3542 | + # Items dimensions variable: relative, absolute, center |
3543 | + self._item_width = item_width |
3544 | + self._item_height = item_height |
3545 | + self._item_width_abs = self.get_abs_x(item_width) |
3546 | + self._item_height_abs = self.get_abs_y(item_height) |
3547 | + self._dx = int(self._item_width_abs / 2) |
3548 | + self._dy = int(self._item_height_abs / 2) |
3549 | + |
3550 | + # Default cursor's index. |
3551 | + self._selected_index = 0 |
3552 | + |
3553 | + # Grid dimensions: real, visible. |
3554 | + self.items_per_row = 10 |
3555 | + self.items_per_col = 10 |
3556 | + self._visible_rows = 3 |
3557 | + self._visible_cols = 5 |
3558 | + |
3559 | + # The moving_group is a Clutter group containing all the items. |
3560 | + self._moving_group_x = 0 |
3561 | + self._moving_group_y = 0 |
3562 | + self._moving_group = clutter.Group() |
3563 | + self.add(self._moving_group) |
3564 | + |
3565 | + # The moving_group is translated using a `BehaviourPath`. |
3566 | + self._moving_group_timeline = clutter.Timeline(fps=200, duration=200) |
3567 | + moving_group_alpha = clutter.Alpha(self._moving_group_timeline, |
3568 | + clutter.smoothstep_inc_func) |
3569 | + self._moving_group_behaviour = clutter.BehaviourPath(moving_group_alpha) |
3570 | + self._moving_group_behaviour.apply(self._moving_group) |
3571 | + |
3572 | + # The cursor is an Actor that can be added and moved on the menu. |
3573 | + # The cusor is always located in the visible (clipped) area of the menu. |
3574 | + self._cursor_x = 0 |
3575 | + self._cursor_y = 0 |
3576 | + self._cursor = None |
3577 | + self._cursor_timeline = clutter.Timeline(fps=200, duration=200) |
3578 | + cursor_alpha = clutter.Alpha(self._cursor_timeline, |
3579 | + clutter.sine_inc_func) |
3580 | + self._cursor_behaviour = clutter.BehaviourPath(cursor_alpha) |
3581 | + |
3582 | + # A MotionBuffer is used to compute useful information about the |
3583 | + # cursor's motion. It's used when moving the cursor with a pointer. |
3584 | + self._motion_buffer = MotionBuffer() |
3585 | + self._event_mode = self.MODE_NONE |
3586 | + self._motion_handler = 0 |
3587 | + self._seek_step_x = 0 |
3588 | + self._seek_step_y = 0 |
3589 | + gobject.timeout_add(200, self._internal_timer_callback) |
3590 | + |
3591 | + #XXX: Samuel Buffet |
3592 | + # This rectangle is used to grab events as it turns out that their |
3593 | + # might be a bug in clutter 0.8 or python-clutter 0.8. |
3594 | + # It may be avoided with next release of clutter. |
3595 | + self._event_rect = clutter.Rectangle() |
3596 | + self._event_rect.set_opacity(0) |
3597 | + self.add(self._event_rect) |
3598 | + self._event_rect.set_reactive(True) |
3599 | + self._event_rect.connect('button-press-event', |
3600 | + self._on_button_press_event) |
3601 | + self._event_rect.connect('button-release-event', |
3602 | + self._on_button_release_event) |
3603 | + self._event_rect.connect('scroll-event', self._on_scroll_event) |
3604 | + |
3605 | + self.set_position(self.get_abs_x(x), self.get_abs_y(y)) |
3606 | + |
3607 | + @property |
3608 | + def count(self): |
3609 | + """Return the number of items.""" |
3610 | + return len(self.items) |
3611 | + |
3612 | + @property |
3613 | + def on_top(self): |
3614 | + """Return True if the selected item is currently on the top.""" |
3615 | + selected_row = self._index_to_xy(self._selected_index)[1] |
3616 | + if selected_row == 0: |
3617 | + return True |
3618 | + else: |
3619 | + return False |
3620 | + |
3621 | + @property |
3622 | + def on_bottom(self): |
3623 | + """Return True if the selected item is currently on the bottom.""" |
3624 | + selected_row = self._index_to_xy(self._selected_index)[1] |
3625 | + if self._is_vertical: |
3626 | + end_row = self._index_to_xy(self.count - 1)[1] |
3627 | + if selected_row == end_row: |
3628 | + return True |
3629 | + else: |
3630 | + return False |
3631 | + else: |
3632 | + if selected_row == self.items_per_col - 1: |
3633 | + return True |
3634 | + else: |
3635 | + return False |
3636 | + |
3637 | + @property |
3638 | + def on_left(self): |
3639 | + """Return True if the selected item is currently on the left.""" |
3640 | + selected_col = self._index_to_xy(self._selected_index)[0] |
3641 | + if selected_col == 0: |
3642 | + return True |
3643 | + else: |
3644 | + return False |
3645 | + |
3646 | + @property |
3647 | + def on_right(self): |
3648 | + """Return True if the selected item is currently on the right.""" |
3649 | + selected_col = self._index_to_xy(self._selected_index)[0] |
3650 | + if not self._is_vertical: |
3651 | + end_col = self._index_to_xy(self.count - 1)[0] |
3652 | + if selected_col == end_col: |
3653 | + return True |
3654 | + else: |
3655 | + return False |
3656 | + else: |
3657 | + if selected_col == self.items_per_row - 1: |
3658 | + return True |
3659 | + else: |
3660 | + return False |
3661 | + |
3662 | + @property |
3663 | + def selected_item(self): |
3664 | + """Return the selected MenuItem.""" |
3665 | + if self.count == 0: |
3666 | + return None |
3667 | + else: |
3668 | + return self.items[self._selected_index] |
3669 | + |
3670 | + @property |
3671 | + def selected_userdata(self): |
3672 | + """Return userdata of the MenuItem.""" |
3673 | + item = self.selected_item |
3674 | + if item is None: |
3675 | + return None |
3676 | + else: |
3677 | + return item.userdata |
3678 | + |
3679 | + def _get_active(self): |
3680 | + """Active property getter.""" |
3681 | + return self._active |
3682 | + |
3683 | + def _set_active(self, boolean): |
3684 | + """Active property setter.""" |
3685 | + if self._active == boolean: |
3686 | + return |
3687 | + |
3688 | + self._active = boolean |
3689 | + if boolean: |
3690 | + if self._cursor is not None: |
3691 | + self._cursor.show() |
3692 | + if self.selected_item is not None: |
3693 | + self.selected_item.animate_in() |
3694 | + self.emit('activated') |
3695 | + self.set_opacity(255) |
3696 | + else: |
3697 | + if self._cursor is not None: |
3698 | + self._cursor.hide() |
3699 | + if self.selected_item is not None: |
3700 | + self.selected_item.animate_out() |
3701 | + self.set_opacity(128) |
3702 | + |
3703 | + active = property(_get_active, _set_active) |
3704 | + |
3705 | + def _get_horizontal(self): |
3706 | + """horizontal property getter.""" |
3707 | + return not self._is_vertical |
3708 | + |
3709 | + def _set_horizontal(self, boolean): |
3710 | + """horizontal property setter.""" |
3711 | + self._is_vertical = not boolean |
3712 | + |
3713 | + horizontal = property(_get_horizontal, _set_horizontal) |
3714 | + |
3715 | + def _get_vertical(self): |
3716 | + """vertical property getter.""" |
3717 | + return self._is_vertical |
3718 | + |
3719 | + def _set_vertical(self, boolean): |
3720 | + """vertical property setter.""" |
3721 | + self._is_vertical = boolean |
3722 | + |
3723 | + vertical = property(_get_vertical, _set_vertical) |
3724 | + |
3725 | + def _get_selected_index(self): |
3726 | + """selected_index property getter.""" |
3727 | + return self._selected_index |
3728 | + |
3729 | + def _set_selected_index(self, index, duration=None): |
3730 | + """selected_index property setter.""" |
3731 | + # Xc, Yc : coordinates of the menu's cursor on the array of items. |
3732 | + # xc, yc : coordinates of the menu's cursor relative to the menu. |
3733 | + # xm, ym : coordinates of the moving_group relative to the menu. |
3734 | + # Xc = xc - xm |
3735 | + # Yc = yc - ym |
3736 | + |
3737 | + if self._selected_index == index or \ |
3738 | + index < 0 or \ |
3739 | + index > self.count - 1 or \ |
3740 | + self._moving_group_timeline.is_playing() or \ |
3741 | + self._cursor_timeline.is_playing(): |
3742 | + return |
3743 | + |
3744 | + # Start select/unselect animations on both items. |
3745 | + self.items[self._selected_index].animate_out() |
3746 | + self.items[index].animate_in() |
3747 | + |
3748 | + # Get the cursor's coordinate on the array. |
3749 | + # /!\ Those coordinates are NOT pixels but refer to the array of items. |
3750 | + (Xc, Yc) = self._index_to_xy(index) |
3751 | + |
3752 | + xm = self._moving_group_x |
3753 | + ym = self._moving_group_y |
3754 | + |
3755 | + xc = Xc + xm |
3756 | + yc = Yc + ym |
3757 | + |
3758 | + # If the targeted cursor's position is on the last visible column then |
3759 | + # the moving_group is translated by -1 on the x axis and the translation |
3760 | + # of the cursor is reduce by 1 to stay on the column before the last |
3761 | + # one. This is not done if the last column has been selected. |
3762 | + if xc == self.visible_cols - 1 and \ |
3763 | + xm > self.visible_cols -self.items_per_row: |
3764 | + xc -= 1 |
3765 | + xm -= 1 |
3766 | + |
3767 | + # If the targeted cursor's position is on the first visible column then |
3768 | + # the moving_group is translated by +1 on the x axis and the translation |
3769 | + # of the cursor is raised by 1 to stay on the column after the first |
3770 | + # one. This is not done if the first column has been selected. |
3771 | + if xc == 0 and xm < 0: |
3772 | + xc += 1 |
3773 | + xm += 1 |
3774 | + |
3775 | + # If the targeted cursor's position is on the last visible row then |
3776 | + # the moving_group is translated by -1 on the y axis and the translation |
3777 | + # of the cursor is reduce by 1 to stay on the row before the last |
3778 | + # one. This is not done if the last row has been selected. |
3779 | + if yc == self.visible_rows - 1 and \ |
3780 | + ym > self.visible_rows -self.items_per_col: |
3781 | + yc -= 1 |
3782 | + ym -= 1 |
3783 | + |
3784 | + # If the targeted cursor's position is on the first visible row then |
3785 | + # the moving_group is translated by +1 on the y axis and the translation |
3786 | + # of the cursor is raised by 1 to stay on the row after the first |
3787 | + # one. This is not done if the last row has been selected. |
3788 | + if yc == 0 and ym < 0: |
3789 | + yc += 1 |
3790 | + ym += 1 |
3791 | + |
3792 | + if duration is None: |
3793 | + duration = self.motion_duration |
3794 | + |
3795 | + self._move_cursor(xc, yc, duration) |
3796 | + self._move_moving_group(xm, ym, duration) |
3797 | + self._selected_index = index |
3798 | + |
3799 | + self.emit('moved') |
3800 | + |
3801 | + selected_index = property(_get_selected_index, _set_selected_index) |
3802 | + |
3803 | + def _get_visible_rows(self): |
3804 | + """visible_rows property getter.""" |
3805 | + return self._visible_rows |
3806 | + |
3807 | + def _set_visible_rows(self, visible_rows): |
3808 | + """visible_rows property setter.""" |
3809 | + self._visible_rows = visible_rows |
3810 | + self._clip() |
3811 | + |
3812 | + visible_rows = property(_get_visible_rows, _set_visible_rows) |
3813 | + |
3814 | + def _get_visible_cols(self): |
3815 | + """visible_cols property getter.""" |
3816 | + return self._visible_cols |
3817 | + |
3818 | + def _set_visible_cols(self, visible_cols): |
3819 | + """visible_cols property setter.""" |
3820 | + self._visible_cols = visible_cols |
3821 | + self._clip() |
3822 | + |
3823 | + visible_cols = property(_get_visible_cols, _set_visible_cols) |
3824 | + |
3825 | + def _get_cursor(self): |
3826 | + """cursor property getter.""" |
3827 | + return self._cursor |
3828 | + |
3829 | + def _set_cursor(self, cursor): |
3830 | + """cursor property setter.""" |
3831 | + if self._cursor is not None: |
3832 | + self.remove(self._cursor) |
3833 | + |
3834 | + self._cursor = cursor |
3835 | + |
3836 | + if self._cursor is not None: |
3837 | + self.add(self._cursor) |
3838 | + if self._active: |
3839 | + self._cursor.show() |
3840 | + else: |
3841 | + self._cursor.hide() |
3842 | + |
3843 | + if self.cursor_below: |
3844 | + self._cursor.lower_bottom() |
3845 | + else: |
3846 | + self._cursor.raise_top() |
3847 | + |
3848 | + self._cursor.set_size(int(self._item_width_abs), |
3849 | + int(self._item_height_abs)) |
3850 | + self._cursor.set_anchor_point(self._dx, self._dy) |
3851 | + self._cursor.set_position(self._dx, self._dy) |
3852 | + |
3853 | + self._cursor_behaviour.apply(self._cursor) |
3854 | + |
3855 | + cursor = property(_get_cursor, _set_cursor) |
3856 | + |
3857 | + def _clip(self): |
3858 | + """Updates the clipping region.""" |
3859 | + self.set_clip(0, 0, self._visible_cols * self._item_width_abs, |
3860 | + self._visible_rows * self._item_height_abs) |
3861 | + |
3862 | + self._event_rect.set_size(self._visible_cols * self._item_width_abs, |
3863 | + self._visible_rows * self._item_height_abs) |
3864 | + |
3865 | + def stop_animation(self): |
3866 | + """Stops the timelines driving menu animation.""" |
3867 | + self._moving_group_timeline.stop() |
3868 | + self._cursor_timeline.stop() |
3869 | + |
3870 | + def raw_add_item(self, item): |
3871 | + """A method to add an item in the menu.""" |
3872 | + self._moving_group.add(item) |
3873 | + self.items.append(item) |
3874 | + |
3875 | + (x, y) = self._index_to_xy(self.count - 1) |
3876 | + |
3877 | + item.move_anchor_point(self._dx, self._dy) |
3878 | + item.set_position(x * self._item_width_abs + self._dx, |
3879 | + y * self._item_height_abs + self._dy) |
3880 | + |
3881 | + if self._is_vertical: |
3882 | + self.items_per_col = y + 1 |
3883 | + else: |
3884 | + self.items_per_row = x + 1 |
3885 | + |
3886 | + if self.cursor_below: |
3887 | + item.raise_top() |
3888 | + else: |
3889 | + item.lower_bottom() |
3890 | + |
3891 | + def _index_to_xy(self, index): |
3892 | + """Return the coordinates of an element associated to its index.""" |
3893 | + if self._is_vertical: |
3894 | + r = index / float(self.items_per_row) |
3895 | + y = int(math.modf(r)[1]) |
3896 | + x = int(index - y * self.items_per_row) |
3897 | + else: |
3898 | + r = index / float(self.items_per_col) |
3899 | + x = int(math.modf(r)[1]) |
3900 | + y = int(index - x * self.items_per_col) |
3901 | + |
3902 | + return (x, y) |
3903 | + |
3904 | + def _move_moving_group(self, x, y, duration): |
3905 | + """Moves the moving_group to x, y coordinates.""" |
3906 | + if (x, y) == (self._moving_group_x, self._moving_group_y): |
3907 | + return |
3908 | + |
3909 | + self._moving_group_behaviour.clear() |
3910 | + |
3911 | + start_knot = (self._moving_group_x * self._item_width_abs, |
3912 | + self._moving_group_y * self._item_height_abs) |
3913 | + stop_knot = (x * self._item_width_abs, |
3914 | + y * self._item_height_abs) |
3915 | + |
3916 | + self._moving_group_behaviour.append_knots(start_knot, stop_knot) |
3917 | + |
3918 | + self._moving_group_x, self._moving_group_y = x, y |
3919 | + self._moving_group_timeline.set_duration(duration) |
3920 | + self._moving_group_timeline.start() |
3921 | + |
3922 | + def _move_cursor(self, x, y, duration): |
3923 | + """ |
3924 | + Moves the cursor to x, y coordinates. |
3925 | + The motion is applied to the center of the cursor. |
3926 | + """ |
3927 | + if (x, y) == (self._cursor_x, self._cursor_y): |
3928 | + return |
3929 | + |
3930 | + self._cursor_behaviour.clear() |
3931 | + |
3932 | + start_knot = (self._cursor_x * self._item_width_abs + self._dx, |
3933 | + self._cursor_y * self._item_height_abs + self._dy) |
3934 | + stop_knot = (x * self._item_width_abs + self._dx, |
3935 | + y * self._item_height_abs + self._dy) |
3936 | + |
3937 | + self._cursor_behaviour.append_knots(start_knot, stop_knot) |
3938 | + |
3939 | + self._cursor_x, self._cursor_y = x, y |
3940 | + self._cursor_timeline.set_duration(duration) |
3941 | + self._cursor_timeline.start() |
3942 | + |
3943 | + def up(self): |
3944 | + """Move the menu's cursor up changing the selected_index property.""" |
3945 | + if not self.on_top: |
3946 | + if self._is_vertical: |
3947 | + self.selected_index -= self.items_per_row |
3948 | + else: |
3949 | + self.selected_index -= 1 |
3950 | + |
3951 | + def down(self): |
3952 | + """Move the menu's cursor down changing the selected_index property.""" |
3953 | + if not self.on_bottom: |
3954 | + if self._is_vertical: |
3955 | + self.selected_index += self.items_per_row |
3956 | + else: |
3957 | + self.selected_index += 1 |
3958 | + |
3959 | + def right(self): |
3960 | + """Move the menu's cursor right changing the selected_index property.""" |
3961 | + if not self.on_right: |
3962 | + if self._is_vertical: |
3963 | + self.selected_index += 1 |
3964 | + else: |
3965 | + self.selected_index += self.items_per_col |
3966 | + |
3967 | + def left(self): |
3968 | + """Move the menu's cursor left changing the selected_index property.""" |
3969 | + if not self.on_left: |
3970 | + if self._is_vertical: |
3971 | + self.selected_index -= 1 |
3972 | + else: |
3973 | + self.selected_index -= self.items_per_col |
3974 | + |
3975 | + def _internal_timer_callback(self): |
3976 | + """ |
3977 | + This callback is used to move the cursor if the SEEK mode is activated. |
3978 | + """ |
3979 | + if self._event_mode == self.MODE_SEEK: |
3980 | + if self._seek_step_x == 1: |
3981 | + self.right() |
3982 | + if self._seek_step_x == -1: |
3983 | + self.left() |
3984 | + if self._seek_step_y == 1: |
3985 | + self.down() |
3986 | + if self._seek_step_y == -1: |
3987 | + self.up() |
3988 | + |
3989 | + return True |
3990 | + |
3991 | + def _on_button_press_event(self, actor, event): |
3992 | + """button-press-event handler.""" |
3993 | + clutter.grab_pointer(self._event_rect) |
3994 | + if not self._event_rect.handler_is_connected(self._motion_handler): |
3995 | + self._motion_handler = self._event_rect.connect('motion-event', |
3996 | + self._on_motion_event) |
3997 | + |
3998 | + (x_menu, y_menu) = self.get_transformed_position() |
3999 | + (x_moving_group, y_moving_group) = self._moving_group.get_position() |
4000 | + |
4001 | + # Events coordinates are relative to the stage. |
4002 | + # So they need to be computed relatively to the moving group. |
4003 | + x = event.x - x_menu - x_moving_group |
4004 | + y = event.y - y_menu - y_moving_group |
4005 | + |
4006 | + x_grid = int(x / self._item_width_abs) |
4007 | + y_grid = int(y / self._item_height_abs) |
4008 | + |
4009 | + if self._is_vertical: |
4010 | + new_index = y_grid * self.items_per_row + x_grid |
4011 | + else: |
4012 | + new_index = x_grid * self.items_per_col + y_grid |
4013 | + |
4014 | + (delta_x, delta_y) = self._index_to_xy(self._selected_index) |
4015 | + |
4016 | + delta_x -= x_grid |
4017 | + delta_y -= y_grid |
4018 | + |
4019 | + # Correction factor due to the fact that items are not necessary square, |
4020 | + # but most probably rectangles. So the distance in the grid coordinates |
4021 | + # must be corrected by a factor to have a real distance in pixels on the |
4022 | + # screen. |
4023 | + correction = float(self._item_width_abs) / float(self._item_height_abs) |
4024 | + correction *= correction |
4025 | + distance = math.sqrt(delta_x ** 2 * correction + delta_y ** 2) |
4026 | + |
4027 | + # Computation of the duration of animations, scaling grid steps to ms. |
4028 | + duration = int(distance * 50) |
4029 | + |
4030 | + if self.selected_index == new_index and \ |
4031 | + self.active and \ |
4032 | + not self._cursor_timeline.is_playing() and \ |
4033 | + not self._moving_group_timeline.is_playing(): |
4034 | + self._event_mode = self.MODE_SELECT |
4035 | + else: |
4036 | + self.active = True |
4037 | + self._event_mode = self.MODE_NONE |
4038 | + |
4039 | + self._set_selected_index(new_index, duration) |
4040 | + |
4041 | + self._motion_buffer.start(event) |
4042 | + |
4043 | + return False |
4044 | + |
4045 | + def _on_button_release_event(self, actor, event): |
4046 | + """button-release-event handler.""" |
4047 | + clutter.ungrab_pointer() |
4048 | + if self._event_rect.handler_is_connected(self._motion_handler): |
4049 | + self._event_rect.disconnect_by_func(self._on_motion_event) |
4050 | + |
4051 | + if self._event_mode == self.MODE_SELECT: |
4052 | + self.emit('selected') |
4053 | + |
4054 | + self._event_mode = self.MODE_NONE |
4055 | + |
4056 | + return True |
4057 | + |
4058 | + def _on_motion_event(self, actor, event): |
4059 | + """motion-event handler""" |
4060 | + # threshold in pixels = the minimum distance we have to move before we |
4061 | + # consider a motion has started |
4062 | + motion_threshold = 20 |
4063 | + |
4064 | + self._seek_step_x = 0 |
4065 | + self._seek_step_y = 0 |
4066 | + self._motion_buffer.compute_from_start(event) |
4067 | + self._motion_buffer.compute_from_last_motion_event(event) |
4068 | + |
4069 | + if self._motion_buffer.distance_from_start > motion_threshold: |
4070 | + self._event_mode = self.MODE_SEEK |
4071 | + self._motion_buffer.take_new_motion_event(event) |
4072 | + dx = self._motion_buffer.dx_from_last_motion_event |
4073 | + dy = self._motion_buffer.dy_from_last_motion_event |
4074 | + |
4075 | + if math.fabs(dx) > math.fabs(dy): |
4076 | + self._seek_step_x = dx > 0 and 1 or -1 |
4077 | + else: |
4078 | + self._seek_step_y = dy > 0 and 1 or -1 |
4079 | + |
4080 | + return False |
4081 | + |
4082 | + def _on_scroll_event(self, actor, event): |
4083 | + """scroll-event handler (mouse's wheel).""" |
4084 | + if not self.active: |
4085 | + self.active = True |
4086 | + return |
4087 | + |
4088 | + if event.direction == clutter.SCROLL_DOWN: |
4089 | + self.down() |
4090 | + else: |
4091 | + self.up() |
4092 | + |
4093 | + return False |
4094 | |
4095 | |
4096 | === modified file 'entertainerlib/frontend/gui/widgets/image_menu.py' |
4097 | --- entertainerlib/frontend/gui/widgets/image_menu.py 2009-05-06 03:40:22 +0000 |
4098 | +++ entertainerlib/frontend/gui/widgets/image_menu.py 2009-07-19 19:27:31 +0000 |
4099 | @@ -2,88 +2,177 @@ |
4100 | '''Implements a grid menu that contains images''' |
4101 | |
4102 | import clutter |
4103 | +import gtk |
4104 | +import gobject |
4105 | |
4106 | from entertainerlib.frontend.gui.widgets.grid_menu import GridMenu |
4107 | -from entertainerlib.utils.configuration import Configuration |
4108 | +from entertainerlib.frontend.gui.widgets.menu_item import MenuItem |
4109 | +from entertainerlib.frontend.gui.widgets.rounded_texture import RoundedTexture |
4110 | +from entertainerlib.frontend.gui.widgets.texture import Texture |
4111 | + |
4112 | +class ImageMenuItem(MenuItem): |
4113 | + """A menuitem widget that contains a Texture.""" |
4114 | + |
4115 | + def __init__(self, width, height, texture): |
4116 | + MenuItem.__init__(self) |
4117 | + |
4118 | + item_width = self.get_abs_x(width) |
4119 | + item_height = self.get_abs_y(height) |
4120 | + |
4121 | + self.original_ratio = float(texture.get_width()) |
4122 | + self.original_ratio /= texture.get_height() |
4123 | + item_ratio = float(item_width) / float(item_height) |
4124 | + |
4125 | + margin = 0.95 |
4126 | + |
4127 | + if item_ratio >= self.original_ratio: |
4128 | + texture_width = self.original_ratio * item_height |
4129 | + texture_height = item_height |
4130 | + texture_width *= margin |
4131 | + texture_height *= margin |
4132 | + delta_width = item_width - texture_width |
4133 | + delta_height = item_height - texture_height |
4134 | + else: |
4135 | + texture_width = item_width |
4136 | + texture_height = item_width / self.original_ratio |
4137 | + texture_width *= margin |
4138 | + texture_height *= margin |
4139 | + delta_width = item_width - texture_width |
4140 | + delta_height = item_height - texture_height |
4141 | + |
4142 | + item_x = delta_width / 2.0 |
4143 | + item_y = delta_height / 2.0 |
4144 | + |
4145 | + texture.set_position(int(item_x), int(item_y)) |
4146 | + texture.set_size(int(texture_width), int(texture_height)) |
4147 | + |
4148 | + self.add(texture) |
4149 | + |
4150 | |
4151 | class ImageMenu(GridMenu): |
4152 | - """ |
4153 | - ImageMenu widget |
4154 | - |
4155 | - This widget inherits genral GridMenu widget and implements simple |
4156 | - imagemenu. |
4157 | - """ |
4158 | - |
4159 | - def __init__(self, theme): |
4160 | - """Initialize widget""" |
4161 | - GridMenu.__init__(self, self.HORIZONTAL) |
4162 | - |
4163 | - #XXX: Joshua: This is to make sure the spaces in an image menu are the |
4164 | - #right size when the screen is not the default size of 1366x768 |
4165 | - #This should eventually be replaced by proportional resizing. |
4166 | - config = Configuration() |
4167 | - |
4168 | - width_ratio = config.get_stage_width() / 1366.0 |
4169 | - height_ratio = config.get_stage_height() / 768.0 |
4170 | - |
4171 | - self.set_item_size(int(200 * width_ratio), int(150 * height_ratio)) |
4172 | - |
4173 | - #End of temporary code |
4174 | - |
4175 | - self.set_row_count(3) |
4176 | + """A grid menu that contains images.""" |
4177 | + __gsignals__ = { |
4178 | + 'filled' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
4179 | + } |
4180 | + |
4181 | + def __init__(self, x=0, y=0, item_width=0.2, item_height=0.1): |
4182 | + GridMenu.__init__(self, x, y, item_width, item_height) |
4183 | + |
4184 | + self.cursor_below = False |
4185 | + self.horizontal = True |
4186 | + self.items_per_col = 4 |
4187 | + self.visible_rows = 3 |
4188 | + self.visible_cols = 5 |
4189 | |
4190 | c = clutter.Rectangle() |
4191 | + c.set_size(100, 100) |
4192 | c.set_color((255, 255, 255, 128)) |
4193 | - self.set_cursor(c) |
4194 | - |
4195 | - def set_animate(self, boolean): |
4196 | - """ |
4197 | - Enable/disable widget animation. |
4198 | - @param boolean: True if widget should be animated, otherwise False |
4199 | - """ |
4200 | - self.animate = boolean |
4201 | - #self.cursor.animate(boolean) |
4202 | - |
4203 | - def _update_selected_item(self, new_location, old_location=None): |
4204 | - """ |
4205 | - Overridden method! See GridMenu API reference. |
4206 | - @param new_location: Logical location of current menuitem |
4207 | - @param old_location: Logical location of last menuitem |
4208 | - """ |
4209 | - if old_location is not None: |
4210 | - self.items[old_location].set_active(False) |
4211 | - self.items[new_location].set_active(True) |
4212 | - |
4213 | - def _animate_items_on_state_change(self): |
4214 | - """ |
4215 | - This method is called when widget is set active or inactive. This |
4216 | - method allows widget to react this action. |
4217 | - """ |
4218 | - if len(self.items): |
4219 | - if self.active: |
4220 | - self.cursor.show() |
4221 | - self.items[self.logical_position].set_active(True) |
4222 | - self.set_opacity(255) |
4223 | - else: |
4224 | - self.cursor.hide() |
4225 | - self.items[self.logical_position].set_active(False) |
4226 | - self.set_opacity(128) |
4227 | - |
4228 | - def _scale_menuitem(self, menuitem): |
4229 | - """ |
4230 | - Scale menuitem to fit into one cell. Preserves actor's aspect ratio. |
4231 | - @param menuitem: clutter.Actor |
4232 | - """ |
4233 | -# w, h = menuitem.get_size() |
4234 | -# |
4235 | -# if w == self.cell_width and h == self.cell_height: |
4236 | -# return # Already correct size, no scale needed |
4237 | -# |
4238 | -# x_ratio = self.cell_width / float(w) |
4239 | -# y_ratio = self.cell_height / float(h) |
4240 | -# if x_ratio > y_ratio: |
4241 | -# menuitem.set_scale(self.cell_height /float(h), |
4242 | -# self.cell_height / float(h)) |
4243 | -# else: |
4244 | -# menuitem.set_scale(self.cell_width / float(w), |
4245 | -# self.cell_width / float(w)) |
4246 | + self.cursor = c |
4247 | + |
4248 | + pix_buffer = gtk.gdk.pixbuf_new_from_file( |
4249 | + self.config.theme.getImage("default_movie_art")) |
4250 | + self.movie_default = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer) |
4251 | + |
4252 | + self.album_default = Texture( |
4253 | + self.config.theme.getImage("default_album_art")) |
4254 | + |
4255 | + def add_item(self, texture, data): |
4256 | + """Add a ImageMenuItem from a Texture.""" |
4257 | + item = ImageMenuItem(self._item_width, self._item_height, texture) |
4258 | + item.userdata = data |
4259 | + |
4260 | + self.raw_add_item(item) |
4261 | + |
4262 | + def async_add(self, items): |
4263 | + """ |
4264 | + Add asynchronously ImageMenuItem using a list. |
4265 | + |
4266 | + The list should be : [[texture1, data1], [texture2, data2], etc] |
4267 | + |
4268 | + texture1, texture2 : are Texture objects. |
4269 | + data1, data2: are the data that will be accessible from the menu item. |
4270 | + (see MenuItem Class) |
4271 | + """ |
4272 | + if len(items) > 0: |
4273 | + item = items[0] |
4274 | + self.add_item(item[0], item[1]) |
4275 | + |
4276 | + # Recursive call, remove first element from the list |
4277 | + gobject.timeout_add(15, self.async_add, items[1:]) |
4278 | + else: |
4279 | + self.emit("filled") |
4280 | + |
4281 | + return False |
4282 | + |
4283 | + # XXX: This needs to be changed. An ImageMenu should know nothing about |
4284 | + # special video items. |
4285 | + def async_add_videos(self, items): |
4286 | + """ |
4287 | + Add asynchronously ImageMenuItem using a list. |
4288 | + The created ImageMenuItem fits movies and series requirements. |
4289 | + See also async_add comments. |
4290 | + """ |
4291 | + if len(items) > 0: |
4292 | + item = items[0] |
4293 | + if item[1].has_cover_art(): |
4294 | + pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0]) |
4295 | + texture = RoundedTexture(0.0, 0.0, 0.1, 0.25, pix_buffer) |
4296 | + else: |
4297 | + texture = clutter.CloneTexture(self.movie_default) |
4298 | + |
4299 | + self.add_item(texture, item[1]) |
4300 | + |
4301 | + # Recursive call, remove first element from the list |
4302 | + gobject.timeout_add(10, self.async_add_videos, items[1:]) |
4303 | + else: |
4304 | + self.emit("filled") |
4305 | + |
4306 | + return False |
4307 | + |
4308 | + # XXX: This needs to be changed. An ImageMenu should know nothing about |
4309 | + # special album items. |
4310 | + def async_add_albums(self, items): |
4311 | + """ |
4312 | + Add asynchronously ImageMenuItem using a list. |
4313 | + The created ImageMenuItem fits albums requirements. |
4314 | + See also async_add comments. |
4315 | + """ |
4316 | + if len(items) > 0: |
4317 | + item = items[0] |
4318 | + if item[1].has_album_art(): |
4319 | + texture = Texture(item[0]) |
4320 | + else: |
4321 | + texture = clutter.CloneTexture(self.album_default) |
4322 | + |
4323 | + self.add_item(texture, item[1]) |
4324 | + |
4325 | + # Recursive call, remove first element from the list |
4326 | + gobject.timeout_add(10, self.async_add_albums, items[1:]) |
4327 | + else: |
4328 | + self.emit("filled") |
4329 | + |
4330 | + return False |
4331 | + |
4332 | + # XXX: This needs to be changed. An ImageMenu should know nothing about |
4333 | + # special clip items. |
4334 | + def async_add_clips(self, items): |
4335 | + """ |
4336 | + Add asynchronously ImageMenuItem using a list. |
4337 | + The created ImageMenuItem fits clips requirements. |
4338 | + See also async_add comments. |
4339 | + """ |
4340 | + if len(items) > 0: |
4341 | + item = items[0] |
4342 | + pix_buffer = gtk.gdk.pixbuf_new_from_file(item[0]) |
4343 | + texture = RoundedTexture(0.0, 0.0, 0.23, self.y_for_x(0.23) * 0.7, |
4344 | + pix_buffer) |
4345 | + |
4346 | + self.add_item(texture, item[1]) |
4347 | + |
4348 | + # Recursive call, remove first element from the list |
4349 | + gobject.timeout_add(10, self.async_add_clips, items[1:]) |
4350 | + else: |
4351 | + self.emit("filled") |
4352 | + |
4353 | + return False |
4354 | + |
4355 | |
4356 | === removed file 'entertainerlib/frontend/gui/widgets/image_menu_item.py' |
4357 | --- entertainerlib/frontend/gui/widgets/image_menu_item.py 2009-05-06 02:58:08 +0000 |
4358 | +++ entertainerlib/frontend/gui/widgets/image_menu_item.py 1970-01-01 00:00:00 +0000 |
4359 | @@ -1,34 +0,0 @@ |
4360 | -# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4361 | -'''Menuitem that contains an image''' |
4362 | - |
4363 | -from entertainerlib.frontend.gui.widgets.menu_item import MenuItem |
4364 | - |
4365 | -class ImageMenuItem(MenuItem): |
4366 | - """ |
4367 | - Simple Image MenuItem |
4368 | - |
4369 | - This menu item contains one Texture that is scaled in such a way that the |
4370 | - texture fills the whole menu item area. Texture aspect ratio is not |
4371 | - preserved. ImageMenuItem is defined in terms of its width with a ratio |
4372 | - determining the height relative to width. This is so the image will scale |
4373 | - predictably on monitors of different aspect ratios. |
4374 | - """ |
4375 | - |
4376 | - def __init__(self, x_percent, y_to_x_ratio, texture): |
4377 | - """Initialize menuitem""" |
4378 | - |
4379 | - MenuItem.__init__(self) |
4380 | - |
4381 | - width = self.get_abs_x(x_percent) |
4382 | - height = self._calculate_height(width, y_to_x_ratio) |
4383 | - |
4384 | - texture.set_size(width, height) |
4385 | - |
4386 | - self.add(texture) |
4387 | - |
4388 | - def _calculate_height(self, width, y_to_x_ratio): |
4389 | - """Calculate pixel height from the ratio of y to x""" |
4390 | - height = int(width * y_to_x_ratio) |
4391 | - |
4392 | - return height |
4393 | - |
4394 | |
4395 | === modified file 'entertainerlib/frontend/gui/widgets/label.py' |
4396 | --- entertainerlib/frontend/gui/widgets/label.py 2009-04-28 22:30:06 +0000 |
4397 | +++ entertainerlib/frontend/gui/widgets/label.py 2009-05-31 15:00:01 +0000 |
4398 | @@ -22,7 +22,7 @@ |
4399 | self._font_size = font_size |
4400 | self._set_font_size(font_size) |
4401 | |
4402 | - self.set_color(self.theme.get_color(color_name)) |
4403 | + self.color = color_name |
4404 | |
4405 | self._position = None |
4406 | self._set_position((x_pos_percent, y_pos_percent)) |
4407 | @@ -32,33 +32,33 @@ |
4408 | if name: |
4409 | self.set_name(name) |
4410 | |
4411 | - self._width = self._init_width() |
4412 | - self._height = self._init_height() |
4413 | - |
4414 | self.set_line_wrap(True) |
4415 | |
4416 | + def _get_color(self): |
4417 | + """color property getter.""" |
4418 | + return self._color |
4419 | + |
4420 | + def _set_color(self, color_name): |
4421 | + """color property setter.""" |
4422 | + self._color = color_name |
4423 | + self.set_color(self.theme.get_color(self._color)) |
4424 | + |
4425 | + color = property(_get_color, _set_color) |
4426 | + |
4427 | def set_size(self, x_percent, y_percent): |
4428 | """Override clutter label set_size to calculate absolute size from a |
4429 | percentage""" |
4430 | - self._width = x_percent |
4431 | - self._height = y_percent |
4432 | clutter.Label.set_size(self, |
4433 | self.get_abs_x(x_percent), |
4434 | self.get_abs_y(y_percent)) |
4435 | |
4436 | - def _init_width(self): |
4437 | - """Generate the width of the label initially before the width property |
4438 | - is set to ensure width has a value""" |
4439 | - return float(self.get_width()) / self.config.get_stage_width() |
4440 | - |
4441 | def _get_width(self): |
4442 | """Return the width as a percent of the stage size""" |
4443 | - return self._width |
4444 | + return float(self.get_width()) / self.config.get_stage_width() |
4445 | |
4446 | def _set_width(self, x_percent): |
4447 | """Set the width based on a hozirontal percent of the stage size. |
4448 | Provide clutter label set_width the absolute size from the percentage""" |
4449 | - self._width = x_percent |
4450 | clutter.Label.set_width(self, self.get_abs_x(x_percent)) |
4451 | |
4452 | width = property(_get_width, _set_width) |
4453 | @@ -80,20 +80,14 @@ |
4454 | |
4455 | position = property(_get_position, _set_position) |
4456 | |
4457 | - def _init_height(self): |
4458 | - """Generate the height of the label initially before the height property |
4459 | - is set to ensure height has a value""" |
4460 | - return float(self.get_height()) / self.config.get_stage_height() |
4461 | - |
4462 | def _get_height(self): |
4463 | """Return the height as a percent of the stage size""" |
4464 | - return self._height |
4465 | + return float(self.get_height()) / self.config.get_stage_height() |
4466 | |
4467 | def _set_height(self, y_percent): |
4468 | """Set the height based on a vertical percent of the stage size. |
4469 | Provide clutter label set_height the absolute size from the percentage |
4470 | """ |
4471 | - self._height = y_percent |
4472 | clutter.Label.set_height(self, self.get_abs_y(y_percent)) |
4473 | |
4474 | height = property(_get_height, _set_height) |
4475 | |
4476 | === removed file 'entertainerlib/frontend/gui/widgets/menu.py' |
4477 | --- entertainerlib/frontend/gui/widgets/menu.py 2009-05-06 03:40:22 +0000 |
4478 | +++ entertainerlib/frontend/gui/widgets/menu.py 1970-01-01 00:00:00 +0000 |
4479 | @@ -1,252 +0,0 @@ |
4480 | -# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4481 | -'''Menu has a list of menuitems and it allows user to select one of them''' |
4482 | - |
4483 | -import clutter |
4484 | - |
4485 | -class Menu(clutter.Group): |
4486 | - """ |
4487 | - Menu widget. |
4488 | - |
4489 | - Blaa blaa... |
4490 | - """ |
4491 | - |
4492 | - def __init__(self): |
4493 | - """ |
4494 | - Initialize menu widget. |
4495 | - """ |
4496 | - clutter.Group.__init__(self) |
4497 | - self.__name = None # Menu name |
4498 | - self.__active = False # Is this menu active |
4499 | - self.__page_zize = 8 # How many items are displayed at the same |
4500 | - # time |
4501 | - self.__loop = False # Should menu loop from last to first |
4502 | - self.__selector = None # Selector widget |
4503 | - self.__items = [] # List of MenuItems |
4504 | - self.__current = 0 # Current position of the menu |
4505 | - self.__item_width = 0 # Menuitem width |
4506 | - self.__item_height = 0 # Menuitem height |
4507 | - |
4508 | - def is_active(self): |
4509 | - """ |
4510 | - Is this menu active. |
4511 | - @return boolean, True if menu is currently active, otherwise False |
4512 | - """ |
4513 | - return self.__active |
4514 | - |
4515 | - def set_active(self, boolean): |
4516 | - """ |
4517 | - Set this widget active or inactive |
4518 | - @param boolean: True to set widget active, False to set inactive |
4519 | - """ |
4520 | - self.__active = boolean |
4521 | - if boolean: |
4522 | - if self.__selector is not None: |
4523 | - self.__selector.show() |
4524 | - self.get_current_menuitem().set_active(True) |
4525 | - self.set_opacity(255) |
4526 | - else: |
4527 | - if self.__selector is not None: |
4528 | - self.__selector.hide() |
4529 | - self.get_current_menuitem().set_active(False) |
4530 | - self.set_opacity(128) |
4531 | - |
4532 | - def add(self, menuitem): |
4533 | - """ |
4534 | - Add new menuitem to the menu. |
4535 | - @param menuitem: Add this menuitem (Menitem Widget) |
4536 | - """ |
4537 | - # First menuitem determines the size of all menuitems (is it good?) |
4538 | - if len(self.__items) == 0: |
4539 | - self.__item_width = menuitem.get_width() |
4540 | - self.__item_height = menuitem.get_height() |
4541 | - |
4542 | - menuitem.set_position(0, len(self.__items) * menuitem.get_height()) |
4543 | - self.__items.append(menuitem) |
4544 | - clutter.Group.add(self, menuitem) |
4545 | - self.update_clip() |
4546 | - |
4547 | - def remove(self, menuitem): |
4548 | - """ |
4549 | - Remove menuitem from the menu. |
4550 | - @param menuitem: Remove this menuitem (Menitem Widget) |
4551 | - """ |
4552 | - clutter.Group.remove(self, menuitem) |
4553 | - for item in self.__items: |
4554 | - if item is menuitem: |
4555 | - del item |
4556 | - |
4557 | - def remove_all(self): |
4558 | - """ |
4559 | - Remove all menuitems from the menu. |
4560 | - @param menuitem: Remove this menuitem (Clutter.Actor) |
4561 | - """ |
4562 | - clutter.Group.remove_all(self) |
4563 | - self.__current = 0 |
4564 | - del self.__selector |
4565 | - self.__selector = None |
4566 | - del self.__items |
4567 | - self.__items = [] |
4568 | - |
4569 | - def set_loop(self, boolean): |
4570 | - """ |
4571 | - Set loop for menu. If loop is true then first menuitem comes after the |
4572 | - last one. |
4573 | - @param boolean: True if menu should loop, otherwise False |
4574 | - """ |
4575 | - self.__loop = boolean |
4576 | - |
4577 | - def is_loop(self): |
4578 | - """ |
4579 | - Is menu in loop mode. |
4580 | - @return: Boolean value |
4581 | - """ |
4582 | - return self.__loop |
4583 | - |
4584 | - def set_menuitem_background(self, texture): |
4585 | - """ |
4586 | - Set texture that is displayed under every menuitem. This is optional. |
4587 | - @param texture: Menuitem background texture. (clutter.Actor) |
4588 | - """ |
4589 | - for index in range(len(self.__items)): |
4590 | - bg = clutter.CloneTexture(texture) |
4591 | - bg.set_size(self.get_width(), self.__item_height) |
4592 | - bg.set_position(0, index * self.__item_height) |
4593 | - bg.set_opacity(128) |
4594 | - bg.set_name("background") |
4595 | - clutter.Group.add(self, bg) |
4596 | - for item in self.__items: |
4597 | - clutter.Group.lower(self, bg, item) |
4598 | - |
4599 | - def set_selector(self, selector): |
4600 | - """ |
4601 | - Set selector texture. Selector is moved over menuitems when user |
4602 | - browses the menu. |
4603 | - @param selector: Selector texture (Clutter.Actor) |
4604 | - """ |
4605 | - selector.set_position(0, 0) |
4606 | - x_scale = self.__item_width / float(selector.get_width()) |
4607 | - y_scale = self.__item_height / float(selector.get_height()) |
4608 | - selector.set_size(self.__item_width, self.__item_height) |
4609 | - selector.set_scale(x_scale, y_scale) |
4610 | - selector.set_name("selector") |
4611 | - self.__selector = selector |
4612 | - clutter.Group.add(self, self.__selector) |
4613 | - for item in self.__items: |
4614 | - if item.get_name() != "background": |
4615 | - clutter.Group.lower(self, selector, item) |
4616 | - |
4617 | - def scroll_up(self): |
4618 | - """ |
4619 | - Scroll menu up (one menuitem). |
4620 | - """ |
4621 | - if self.__current > 0: |
4622 | - self.__items[self.__current].set_active(False) |
4623 | - self.__current = self.__current - 1 |
4624 | - self.__items[self.__current].set_active(True) |
4625 | - if self.__selector is not None: |
4626 | - self.__selector.set_position(0, |
4627 | - self.__current * self.__item_height) |
4628 | - |
4629 | - # Loop from top to bottom |
4630 | - elif self.__current == 0 and self.__loop: |
4631 | - self.__items[self.__current].set_active(False) |
4632 | - self.__current = len(self.__items) - 1 |
4633 | - self.__items[self.__current].set_active(True) |
4634 | - if self.__selector is not None: |
4635 | - self.__selector.set_position(0, |
4636 | - self.__current * self.__item_height) |
4637 | - |
4638 | - def scroll_down(self): |
4639 | - """ |
4640 | - Scroll menu down (one menuitem). |
4641 | - """ |
4642 | - if self.__current < len(self.__items) - 1: |
4643 | - self.__items[self.__current].set_active(False) |
4644 | - self.__current = self.__current + 1 |
4645 | - self.__items[self.__current].set_active(True) |
4646 | - if self.__selector is not None: |
4647 | - self.__selector.set_position(0, |
4648 | - self.__current * self.__item_height) |
4649 | - |
4650 | - # Loop from bottom to top |
4651 | - elif self.__current == len(self.__items) - 1 and self.__loop: |
4652 | - self.__items[self.__current].set_active(False) |
4653 | - self.__current = 0 |
4654 | - self.__items[self.__current].set_active(True) |
4655 | - if self.__selector is not None: |
4656 | - self.__selector.set_position(0, |
4657 | - self.__current * self.__item_height) |
4658 | - |
4659 | - def scroll_up_page(self): |
4660 | - """ |
4661 | - Scroll menu up one page. One page is a number of visible items. |
4662 | - """ |
4663 | - |
4664 | - def scroll_down_page(self): |
4665 | - """ |
4666 | - Scroll menu down one page. One page is a number of visible items. |
4667 | - """ |
4668 | - # Set current menuitem |
4669 | - self.__items[self.__current].set_active(False) |
4670 | - self.__current = self.__current + 8 |
4671 | - self.__items[self.__current].set_active(True) |
4672 | - |
4673 | - # Set selector position |
4674 | - if self.__selector is not None: |
4675 | - self.__selector.set_position(0, |
4676 | - self.__current * self.__items[0].get_height()) |
4677 | - |
4678 | - # Clip to current position of the menu |
4679 | - #self.set_clip(0,) |
4680 | - |
4681 | - def update_clip(self): |
4682 | - """ |
4683 | - Update menu clipping. This method decides which partion of menu is |
4684 | - actually displayed on screen. If you have a long menu, not all items |
4685 | - fit into the window. This function checks what is the current position |
4686 | - of the menulist and then clips the widget in such a way that current |
4687 | - position is on screen. |
4688 | - """ |
4689 | - if len(self.__items) <= self.__page_zize: |
4690 | - self.remove_clip() |
4691 | - else: |
4692 | - self.set_clip(0, |
4693 | - self.__current * self.__item_height, |
4694 | - self.__item_width, |
4695 | - self.__item_height * self.__page_zize) |
4696 | - self.set_height(self.__item_height * self.__page_zize) |
4697 | - |
4698 | - def get_current_position(self): |
4699 | - """ |
4700 | - Get current position of the list. This position is under selector. |
4701 | - """ |
4702 | - return self.__current |
4703 | - |
4704 | - def get_current_menuitem(self): |
4705 | - """ |
4706 | - Get Clutter.Actor object from the current postition. |
4707 | - """ |
4708 | - if len(self.__items) == 0: |
4709 | - return None |
4710 | - return self.__items[self.__current] |
4711 | - |
4712 | - def get_number_of_items(self): |
4713 | - """ |
4714 | - Get number of menuitems in menu. |
4715 | - """ |
4716 | - return len(self.__items) |
4717 | - |
4718 | - def set_name(self, name): |
4719 | - """ |
4720 | - Set menu name |
4721 | - @param name: Name of the menu (used to identify menu from others) |
4722 | - """ |
4723 | - self.__name = name |
4724 | - |
4725 | - def get_name(self): |
4726 | - """ |
4727 | - Get name of the menu. |
4728 | - @return name as String |
4729 | - """ |
4730 | - return self.__name |
4731 | - |
4732 | |
4733 | === modified file 'entertainerlib/frontend/gui/widgets/menu_item.py' |
4734 | --- entertainerlib/frontend/gui/widgets/menu_item.py 2009-05-06 03:40:22 +0000 |
4735 | +++ entertainerlib/frontend/gui/widgets/menu_item.py 2009-05-28 19:28:56 +0000 |
4736 | @@ -1,51 +1,34 @@ |
4737 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4738 | -'''Simple Menuitem''' |
4739 | +"""Simple Menuitem.""" |
4740 | |
4741 | import clutter |
4742 | |
4743 | from entertainerlib.frontend.gui.widgets.base import Base |
4744 | |
4745 | class MenuItem(Base, clutter.Group): |
4746 | - """ |
4747 | - Simple menuitem widget. |
4748 | - |
4749 | - This is a base class for all menuitem types. MenuItems are supposed to be |
4750 | - used in Menus which inherit GridMenu class. |
4751 | - """ |
4752 | + """Simple menuitem widget.""" |
4753 | |
4754 | def __init__(self): |
4755 | - """Initialize menuitem.""" |
4756 | - |
4757 | Base.__init__(self) |
4758 | clutter.Group.__init__(self) |
4759 | - self.active = False |
4760 | - self.data = None |
4761 | - |
4762 | - def set_active(self, boolean): |
4763 | - """ |
4764 | - Set menuitem active / inactive. |
4765 | - @param boolean: use True to set active and False to set inactive |
4766 | - """ |
4767 | - self.active = boolean |
4768 | - |
4769 | - def is_active(self): |
4770 | - """ |
4771 | - Get widget status. |
4772 | - @return True if widget is active, otherwise False |
4773 | - """ |
4774 | - return self.active |
4775 | - |
4776 | - def set_userdata(self, data): |
4777 | - """ |
4778 | - Set user data for this menuitem. |
4779 | - @param id: ID string |
4780 | - """ |
4781 | - self.data = data |
4782 | - |
4783 | - def get_userdata(self): |
4784 | - """ |
4785 | - Get user data |
4786 | - @return: string |
4787 | - """ |
4788 | - return self.data |
4789 | + |
4790 | + self._userdata = None |
4791 | + |
4792 | + def _get_userdata(self): |
4793 | + """userdata property getter.""" |
4794 | + return self._userdata |
4795 | + |
4796 | + def _set_userdata(self, userdata): |
4797 | + """userdata property getter.""" |
4798 | + self._userdata = userdata |
4799 | + |
4800 | + userdata = property (_get_userdata, _set_userdata) |
4801 | + |
4802 | + def animate_in(self): |
4803 | + """Animation to be done when an item gets selected.""" |
4804 | + pass |
4805 | + |
4806 | + def animate_out(self): |
4807 | + """Animation to be done when an item gets unselected.""" |
4808 | + pass |
4809 | |
4810 | |
4811 | === modified file 'entertainerlib/frontend/gui/widgets/scroll_area.py' |
4812 | --- entertainerlib/frontend/gui/widgets/scroll_area.py 2009-07-11 19:34:42 +0000 |
4813 | +++ entertainerlib/frontend/gui/widgets/scroll_area.py 2009-07-22 20:46:29 +0000 |
4814 | @@ -1,202 +1,270 @@ |
4815 | # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2 |
4816 | -'''Container that has scrollbars and allows content to be scrolled''' |
4817 | +'''Container that has scrollbars and allows content to be scrolled.''' |
4818 | |
4819 | import clutter |
4820 | +import gobject |
4821 | |
4822 | from entertainerlib.frontend.gui.widgets.base import Base |
4823 | from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator |
4824 | +from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer |
4825 | +from entertainerlib.frontend.gui.widgets.special_behaviours import \ |
4826 | + LoopedPathBehaviour |
4827 | |
4828 | class ScrollArea(Base, clutter.Group): |
4829 | """Wrapper of a clutter Group that allows for scrolling. ScrollArea |
4830 | modifies the width of the content and it assumes that the content uses |
4831 | percent modification (read: not default clutter objects).""" |
4832 | - |
4833 | - def __init__(self, x_size_percent, y_size_percent, x_pos_percent, |
4834 | - y_pos_percent, group, step_size_percent=0.04, active=False): |
4835 | + __gsignals__ = { |
4836 | + 'activated' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
4837 | + 'moving' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ), |
4838 | + } |
4839 | + |
4840 | + MODE_SELECTION = 0 |
4841 | + MODE_MOTION = 1 |
4842 | + MODE_STOP = 2 |
4843 | + STEP_SIZE_PERCENT = 0.04 |
4844 | + |
4845 | + def __init__(self, x, y, width, height, content): |
4846 | Base.__init__(self) |
4847 | clutter.Group.__init__(self) |
4848 | |
4849 | - self.offset = 0 # Determines current position of the view |
4850 | - self.step_size = self.get_abs_y(step_size_percent) |
4851 | - self.area_width = self.get_abs_x(x_size_percent) |
4852 | - self.area_height = self.get_abs_y(y_size_percent) |
4853 | - self.active = active |
4854 | - self.content_group = None |
4855 | - |
4856 | - self.set_position( |
4857 | - self.get_abs_x(x_pos_percent), |
4858 | - self.get_abs_y(y_pos_percent)) |
4859 | + self._motion_buffer = MotionBuffer() |
4860 | + self._offset = 0 # Drives the content motion. |
4861 | + self._offset_max = 0 # Maximum value of offset (equal on bottom). |
4862 | + self._old_offset = 0 # Stores the old value of offset on motions. |
4863 | + self._motion_handler = 0 |
4864 | + self._active = None |
4865 | + |
4866 | + self.step_size = self.get_abs_y(self.STEP_SIZE_PERCENT) |
4867 | + |
4868 | + # Allowed area for the widget's scrolling content. |
4869 | + self.area_width = self.get_abs_x(width) |
4870 | + self.area_height = self.get_abs_y(height) |
4871 | |
4872 | # Create content position indicator |
4873 | - self.indicator = ListIndicator(3 * x_size_percent / 4, y_size_percent, |
4874 | - 0.2, 0.045, ListIndicator.VERTICAL) |
4875 | + self.indicator = ListIndicator(3 * width / 4, height, 0.2, 0.045, |
4876 | + ListIndicator.VERTICAL) |
4877 | self.indicator.hide_position() |
4878 | + self.indicator.set_maximum(2) |
4879 | self.add(self.indicator) |
4880 | |
4881 | - if active: |
4882 | - self.set_active(active) |
4883 | - |
4884 | - self.set_content(group) |
4885 | - |
4886 | - def set_active(self, boolean): |
4887 | - """ |
4888 | - Set this widget active / inactive. When inactive indicator is hidden. |
4889 | - """ |
4890 | - self.active = boolean |
4891 | + # A clipped Group to receive the content. |
4892 | + self._fixed_group = clutter.Group() |
4893 | + self._fixed_group.set_clip(0, 0, self.area_width, self.area_height) |
4894 | + self.add(self._fixed_group) |
4895 | + self.content = None |
4896 | + |
4897 | + self._motion_timeline = clutter.Timeline(fps=200, duration=500) |
4898 | + self._motion_timeline.connect('completed', |
4899 | + self._motion_timeline_callback, None) |
4900 | + self._motion_alpha = clutter.Alpha(self._motion_timeline, |
4901 | + clutter.sine_inc_func) |
4902 | + self._motion_behaviour = LoopedPathBehaviour(self._motion_alpha) |
4903 | + |
4904 | + self.set_content(content) |
4905 | + |
4906 | + self.active = None |
4907 | + |
4908 | + # Preparation to pointer events handling. |
4909 | + self.set_reactive(True) |
4910 | + self.connect('button-press-event', self._on_button_press_event) |
4911 | + self.connect('button-release-event', self._on_button_release_event) |
4912 | + self.connect('scroll-event', self._on_scroll_event) |
4913 | + |
4914 | + self.set_position(self.get_abs_x(x), self.get_abs_y(y)) |
4915 | + |
4916 | + @property |
4917 | + def on_top(self): |
4918 | + """True if we're on top.""" |
4919 | + return self._offset == 0 |
4920 | + |
4921 | + @property |
4922 | + def on_bottom(self): |
4923 | + """True if we're on bottom.""" |
4924 | + return self._offset == self._offset_max |
4925 | + |
4926 | + def _get_active(self): |
4927 | + """Active property getter.""" |
4928 | + return self._active |
4929 | + |
4930 | + def _set_active(self, boolean): |
4931 | + """Active property setter.""" |
4932 | + if self._active == boolean: |
4933 | + return |
4934 | + |
4935 | + self._active = boolean |
4936 | if boolean: |
4937 | - self.indicator.show() |
4938 | + # Show indicator if there is need for scrolling. |
4939 | + if self._offset_max >= 0: |
4940 | + self.indicator.show() |
4941 | + |
4942 | + self.set_opacity(255) |
4943 | + self.emit('activated') |
4944 | else: |
4945 | self.indicator.hide() |
4946 | - |
4947 | - def is_active(self): |
4948 | - """ |
4949 | - Is this widget active. |
4950 | - """ |
4951 | - return self.active |
4952 | - |
4953 | - def set_content(self, group): |
4954 | - """ |
4955 | - Set content into scroll area. |
4956 | - @param group: Group of clutter Actors (clutter.Group) |
4957 | - """ |
4958 | - if self.content_group is not None: |
4959 | - self.remove(self.content_group) |
4960 | - |
4961 | - self.content_group = group |
4962 | - self.add(group) |
4963 | - self.content_group.set_clip(0, 0, self.area_width, self.area_height) |
4964 | - tmp = (self.content_group.get_height() - |
4965 | - self.area_height) / self.step_size |
4966 | - self.indicator.set_maximum(tmp + 2) |
4967 | - |
4968 | - # Do not show indicator if there is no need for scrolling |
4969 | - if self.content_group.get_height() < self.area_height: |
4970 | - self.indicator.hide() |
4971 | - |
4972 | - def set_step_size(self, size_percent): |
4973 | - """Step size determines how much content is scrolled once.""" |
4974 | - |
4975 | - self.step_size = self.get_abs_y(size_percent) |
4976 | - if self.content_group is not None: |
4977 | - tmp = (self.content_group.get_height() - |
4978 | - self.area_height) / self.step_size |
4979 | - self.indicator.set_maximum(tmp + 2) |
4980 | - |
4981 | - def get_step_size(self): |
4982 | - """ |
4983 | - Get step size |
4984 | - @requires: Step size (Integer) |
4985 | - """ |
4986 | - return self.step_size |
4987 | - |
4988 | - def get_offset(self): |
4989 | - """ |
4990 | - Get current offset value. This value determines how many pixels current |
4991 | - view is from the absolute top. |
4992 | - @return: Integer |
4993 | - """ |
4994 | - return self.offset |
4995 | + self.set_opacity(128) |
4996 | + |
4997 | + active = property(_get_active, _set_active) |
4998 | + |
4999 | + def _get_offset(self): |
5000 | + """Get current offset value.""" |
Matt,
Same story than others but with the ScrollArea widget.
The core has been refactored to be driven by a property called "offset". press_event" , "MotionBuffer" etc.
Old friends are all gathered: "active", "on_top", "on_bottom", "_on_button_
Screens using this widget have been updated. The lyrics tab also (small fix included).
The test is updated.
Well this should be routine.
Note: I've merged this branch with "reactive_menus" which is not yet merged with trunk (you were right about the distance of the wheels :)) so the diff provided by lp shouldn't be trusted.
diff vs "reactive_menus": /frontend/ gui/screens/ feed_entry. py' frontend/ gui/screens/ feed_entry. py 2009-06-21 22:38:01 +0000 frontend/ gui/screens/ feed_entry. py 2009-06-25 18:56:04 +0000
entry_ text.set_ use_markup( True)
entry_ text.set_ size(0. 5857, 0.5208)
=== modified file 'entertainerlib
--- entertainerlib/
+++ entertainerlib/
@@ -71,8 +71,8 @@
- self.scroll_area = ScrollArea(0.5857, 0.4948, 0.2072, 0.3,
self. add(self. scroll_ area)
- entry_text, active=True)
+ self.scroll_area = ScrollArea(0.2072, 0.3, 0.5857, 0.4948,
+ entry_text)
# Add the additional actions that are needed but not handled by default
=== modified file 'entertainerlib /frontend/ gui/screens/ movie.py' frontend/ gui/screens/ movie.py 2009-07-19 19:27:31 +0000 frontend/ gui/screens/ movie.py 2009-07-21 21:51:16 +0000
plot. set_line_ wrap_mode( pango.WRAP_ WORD)
plot. set_line_ wrap(True)
self. add(self. scroll_ area)
--- entertainerlib/
+++ entertainerlib/
@@ -109,7 +109,7 @@
plot.width = 0.5124
- self.scroll_area = ScrollArea(0.5124, 0.3516, 0.33, 0.38, plot)
+ self.scroll_area = ScrollArea(0.33, 0.38, 0.5124, 0.3516, plot)
# Actors NAVIGATE_ LEFT.'' '
self. menu.active = True area.set_ active( False) area.active = False
@@ -178,12 +178,12 @@
def _handle_left(self):
'''Handle UserEvent.
- self.scroll_
+ self.scroll_
def _handle_ right(self) : NAVIGATE_ RIGHT.' ''
self. menu.active = False area.set_ active( True) area.active = True
'''Handle UserEvent.
- self.scroll_
+ self.scroll_
def _handle_ select( self, event=None): NAVIGATE_ SELECT. '''
'''Handle UserEvent.
=== modified file 'entertainerlib /frontend/ gui/screens/ tv_episodes. py' frontend/ gui/screens/ tv_episodes. py 2009-07-19 19:27:31 +0000 frontend/ gui/screens/ tv_episodes. py 2009-07-22 19:50:12 +0000
--- entertainerlib/
+++ entertainerlib/
@@ -44,6 +44,7 @@
+ self.menu.
def _create_ episode_ menu(self) :
self. menu.selected_ userdata. get_plot( ))
"""Create a list of available seasons."""
@@ -108,7 +109,8 @@
plot.width = 0.4
- self.scroll_area = ScrollArea(0.4, 0.15, 0.05, 0.63, plot) area.connect( "activated" , self._on_ scroll_ area_activated)
+ self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot)
+ self.scroll_
...